Establishing a reliable remote login to Raspberry Pi boards is the foundational skill for any headless embedded deployment. Whether you are running a home automation hub, a remote weather station, or an MQTT broker, you need secure, predictable terminal access without relying on a physical monitor and keyboard. With the release of Raspberry Pi OS 'Bookworm', the underlying network stack shifted from dhcpcd to NetworkManager, and the display server moved to Wayland—changes that broke many legacy remote access tutorials.

This guide targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm, 64-bit). We will cover the exact steps for headless SSH provisioning, debug the most common connection failures with precise terminal outputs, and build a hardware GPIO network monitor so you never have to guess if your Pi has dropped off the network.

Remote Access Protocols Compared

Before configuring your board, you must select the right protocol for your use case. Terminal access is lightweight, but GUI access requires significantly more bandwidth and different port configurations. Below is a data-dense comparison of the four primary methods to remote login to Raspberry Pi hardware in 2026.

Protocol / Tool Default Port Bandwidth Overhead Primary Use Case Security Posture
OpenSSH (Terminal) 22 (TCP) < 5 Kbps CLI administration, log checking, script execution High (ED25519 keys)
RealVNC / WayVNC 5900 (TCP) 500 Kbps - 5 Mbps GUI interaction, pixel-level debugging on Wayland Medium (Requires SSH tunnel)
Tailscale SSH N/A (Mesh) < 5 Kbps Remote access across NAT/CGNAT without port forwarding Very High (Zero Trust, mTLS)
Cockpit (Web) 9090 (TCP) 50 - 200 Kbps Browser-based system metrics, storage, and service management High (HTTPS / Certs)

For 95% of embedded projects, OpenSSH is the correct baseline. It is lightweight, survives network hiccups better than VNC, and can be secured with cryptographic keys. According to the official Raspberry Pi remote access documentation, SSH is disabled by default on fresh Bookworm images to prevent brute-force attacks on default credentials.

Headless Boot and SSH Key Configuration

Do not rely on password authentication for a remote login to Raspberry Pi deployments. Passwords are vulnerable to brute-force bots scanning port 22. Instead, inject your public key during the imaging phase.

  1. Generate an ED25519 Key Pair: On your host machine, open a terminal and run:
    ssh-keygen -t ed25519 -C "pi5-headless-deploy"
    Press Enter to accept the default file path. ED25519 is faster and more secure than legacy RSA-2048.
  2. Configure Raspberry Pi Imager: Open the Imager, select your Pi 5 board, and choose Raspberry Pi OS (64-bit). Press Ctrl+Shift+X (or click the gear icon) to open Advanced Options.
  3. Enable Services: Check 'Enable SSH' and select 'Use public key authentication'. Paste the contents of your ~/.ssh/id_ed25519.pub file into the text box.
  4. Set Hostname: Change the hostname to something specific like pi5-sensor-hub to avoid mDNS collisions on crowded networks.
  5. Flash and Boot: Write the image, insert the microSD (or NVMe SSD via the Pi 5 PCIe HAT), and apply power. The Pi will boot, inject the key, and start the sshd daemon automatically.

Debugging 'Connection Refused' and Timeouts

When your remote login to Raspberry Pi fails, the terminal output tells you exactly where the packet is dying. Here are the two most common exact error strings and how to fix them.

Error 1: The 'Connection Refused' Rejection

Exact Error String:
ssh: connect to host 192.168.1.42 port 22: Connection refused

This means your computer successfully reached the Pi's IP address on the network, but the Pi's operating system actively rejected the connection on port 22. The host is up, but the door is locked.

The First Three Things to Check:

  1. Is the SSH daemon actually running? Bookworm disables it by default if you missed the Imager step. If you have physical access or a serial console, run sudo systemctl enable --now ssh.
  2. Is a local firewall blocking it? If you previously enabled ufw (Uncomplicated Firewall), it blocks incoming port 22 by default. Fix it via serial: sudo ufw allow ssh.
  3. Did you target the wrong IP? Check your router's DHCP lease table. If the Pi rebooted and grabbed a new IP, you might be knocking on the door of a printer or a smart TV that doesn't run an SSH server.

Error 2: The 'Connection Timed Out' Void

Exact Error String:
ssh: connect to host 192.168.1.42 port 22: Connection timed out

This is a Layer 2 or Layer 3 failure. The packet left your computer but never received a TCP SYN-ACK response. The Pi is either offline, on a different subnet, or suffering a power brownout.

Ranked Causes:

  1. Power Supply Brownout: The Pi 5 requires a 27W USB-C PD power supply to maintain full peripheral and network stability. If you are using a standard 15W phone charger, the board may throttle or drop the Ethernet PHY under load. Check the syslog for Under-voltage detected warnings.
  2. WiFi Credential Failure: If running headless over WiFi, a typo in the NetworkManager configuration will leave the board isolated. Connect a monitor to verify nmcli device wifi connect <SSID> password <PASSWORD>.
  3. VLAN / Subnet Isolation: If your Pi is on an IoT VLAN (e.g., 192.168.20.x) and your PC is on the main LAN (192.168.1.x), your router's firewall will drop the SSH attempt unless explicit inter-VLAN routing rules are configured.

Hardware Network Monitor: GPIO Mapping and Code

When a headless Pi is mounted in an attic, a crawlspace, or a weatherproof outdoor enclosure, you cannot easily plug in a monitor to see if it has dropped off the network. To solve this, we will wire a physical status LED to the GPIO header that stays solid when the network is up, and blinks when the connection drops.

Parts List

  • Board: Raspberry Pi 5 (8GB) running Bookworm 64-bit
  • Power: Official 27W USB-C PD Power Supply
  • Indicator: 5mm Green Diffused LED
  • Current Limiting: 330Ω through-hole resistor (1/4W)
  • Wiring: 2x Female-to-Male jumper wires, half-size breadboard

Pin Mapping Table

Component Pi 5 Physical Pin BCM GPIO Number Function
LED Anode (via 330Ω Resistor) 11 GPIO 17 Network Status Output (HIGH = Connected)
LED Cathode 9 GND Ground Reference

Python Network Monitor Script

This script uses the gpiozero library (pre-installed on Bookworm) and the native socket library to test connectivity. It targets the Pi 5 hardware directly.

import socket
import time
import logging
from gpiozero import LED

# Configure logging for systemd journal integration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Target Board: Raspberry Pi 5 (Bookworm 64-bit)
# Pin Definitions
STATUS_LED_PIN = 17
status_led = LED(STATUS_LED_PIN)

def check_internet(host='1.1.1.1', port=53, timeout=3):
    """
    Check internet connectivity via TCP socket to Cloudflare DNS.
    Using TCP port 53 is faster and more reliable than ICMP ping in Python.
    """
    try:
        socket.setdefaulttimeout(timeout)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, port))
        s.close()
        return True
    except (socket.timeout, socket.error, OSError) as e:
        logging.error(f'Network check failed: {e}')
        return False

def main():
    logging.info(f'Starting Network Monitor on GPIO {STATUS_LED_PIN}')
    try:
        while True:
            if check_internet():
                status_led.on()
                time.sleep(10) # Check every 10 seconds if healthy
            else:
                # Blink manually to indicate fault state
                status_led.on()
                time.sleep(0.25)
                status_led.off()
                time.sleep(0.25)
                # Loop immediately to re-check network without long sleep
    except KeyboardInterrupt:
        logging.info('Monitor stopped by user.')
    except Exception as e:
        logging.critical(f'Unexpected fatal error: {e}')
    finally:
        # Ensure GPIO resources are released cleanly on exit
        status_led.off()
        status_led.close()
        logging.info('GPIO cleanup complete.')

if __name__ == '__main__':
    main()

To run this on boot, save it as /opt/netmon.py and create a systemd service file (/etc/systemd/system/netmon.service) configured to restart on failure.

Extending and Simplifying the Build

Depending on your deployment environment, you may want to strip this setup down to its bare essentials or expand it into a full remote telemetry node.

How to Simplify: Zero-Config Mesh Networking

If you are deploying the Pi 5 on a cellular hotspot, behind a strict university firewall, or on a CGNAT (Carrier-Grade NAT) connection where port forwarding is impossible, simplify your remote login by installing Tailscale. By running curl -fsSL https://tailscale.com/install.sh | sh and authenticating, your Pi joins a secure WireGuard mesh network. You can then remote login using the Tailscale MagicDNS name (e.g., ssh user@pi5-sensor-hub) from anywhere in the world, completely bypassing local router configurations and public IP exposure.

How to Extend: I2C OLED Telemetry Display

If you want to eliminate the need to scan your network for the Pi's IP address entirely, extend the hardware monitor by adding a SSD1306 128x64 I2C OLED display. Wire the SDA pin to GPIO 2 (Physical Pin 3) and SCL to GPIO 3 (Physical Pin 5). Using the luma.oled Python library, you can modify the script above to render the current IP address, CPU temperature, and SSH login attempt count directly onto the screen. This is highly recommended for portable Pi clusters or edge-computing kits that frequently change physical locations and DHCP subnets.