To SSH into a Raspberry Pi, you must enable the SSH daemon (either via the Raspberry Pi Imager advanced settings or by placing an empty ssh file in the /boot/firmware/ partition), connect the board to your network, locate its IP address, and run ssh username@ip_address from your host machine's terminal. If you are running the latest Raspberry Pi OS Bookworm on a Pi 5, legacy tutorials will fail you—the default pi user is gone, and the boot partition path has changed.

This guide cuts through outdated advice. We will cover the exact hardware configuration for the Pi 5, the precise steps for a headless deployment, the exact error strings you will see when things break, and a Python-based hardware watchdog to recover a hung SSH daemon without needing a monitor.

Hardware & Network Spec Sheet: Raspberry Pi 5 (Bookworm)

Before writing a single command, you need to know the baseline environment. The transition to Debian Bookworm changed the underlying network stack and boot partition layout. Here is the exact specification sheet for the target environment of this guide.

Parameter Specification / Value Notes & Edge Cases
Target Board Raspberry Pi 5 (8GB Variant) Requires active cooling (Active Cooler) for sustained network/CPU loads.
Operating System Raspberry Pi OS Bookworm (64-bit) Uses NetworkManager instead of the legacy dhcpcd.
Boot Partition Path /boot/firmware/ Older guides say /boot/. This will fail on Bookworm.
Default SSH Port 22 (TCP) Change to 2222+ in /etc/ssh/sshd_config to reduce botnet noise.
mDNS Suffix .local (e.g., raspberrypi.local) Requires Avahi/Bonjour on the host machine. Windows may need Bonjour Print Services.
Serial Console Fallback GPIO 14 (TXD) / GPIO 15 (RXD) Use a 3.3V USB-to-TTL serial cable if the network stack completely fails.

Step-by-Step Headless SSH Configuration

A 'headless' setup means booting the Pi without a monitor, keyboard, or mouse. Because Raspberry Pi OS disables the SSH daemon by default for security, you must explicitly enable it before the first boot.

Pro-Tip: Always use the Raspberry Pi Imager's advanced settings (the gear icon) to configure SSH. It handles user creation, WiFi credentials, and SSH key injection in one pass, eliminating 90% of first-boot networking errors.

Method 1: The Raspberry Pi Imager (Recommended)

  1. Open Raspberry Pi Imager on your host PC and select Raspberry Pi 5 as the device.
  2. Choose Raspberry Pi OS (64-bit) as the OS.
  3. Select your microSD card or NVMe SSD (via USB adapter).
  4. Click the Edit Settings (gear) button. If prompted to apply default settings, choose No.
  5. Under the General tab, set a unique hostname (e.g., pi-node-01), create a custom username and password, and configure your WiFi SSID/password.
  6. Switch to the Services tab, check Enable SSH, and select Use password authentication (or inject your public RSA/Ed25519 key for better security).
  7. Click Save and write the image. Insert the drive into the Pi 5 and apply power.

Method 2: The Manual ssh File Drop

If you have already flashed the OS and need to enable SSH manually:

  1. Insert the flashed SD card into your PC. It will mount as a drive named bootfs.
  2. Navigate to the root of this drive. (On the Pi itself, this maps to /boot/firmware/).
  3. Create a completely empty file named ssh (no file extension like .txt).
  4. Eject the drive, insert it into the Pi, and boot. The OS will detect the file, enable the SSH daemon, and delete the file.
  5. Note: You still need a way to authenticate. If you didn't create a user via Imager, you must also create a userconf file containing a hashed password, or connect a monitor to set it up on first boot.

The First Three Things to Check When SSH Fails

When you type ssh myuser@raspberrypi.local and it fails, do not blindly reboot the board. Read the exact error string. Here are the three most common failure modes on Bookworm, ranked by frequency, and exactly how to fix them.

1. Error: ssh: connect to host 192.168.x.x port 22: Connection refused

  • Cause: The Pi is on the network and responding to pings, but the SSH daemon (sshd) is not running or is blocked by a local firewall.
  • Fix: You likely missed the ssh file step, or the file had a hidden .txt extension. If you have physical access, plug in a monitor, open a terminal, and run sudo systemctl enable --now ssh. If you are headless, power down, mount the SD card on your PC, and verify the empty ssh file is in the root of the bootfs partition.

2. Error: Permission denied (publickey)

  • Cause: The SSH daemon is running, but it rejected your credentials. On Bookworm, this almost always happens because users try to log in as pi (which no longer exists) or because password authentication is disabled and your host machine's ~/.ssh/id_rsa.pub key wasn't injected properly.
  • Fix: Ensure you are using the custom username you created in the Imager. If using keys, verify permissions on your host machine: your private key must be chmod 600. If you forced key-only auth but lost the key, you must re-flash the OS or mount the SD card to manually edit /etc/ssh/sshd_config to temporarily allow PasswordAuthentication yes.

3. Error: ssh: Could not resolve hostname raspberrypi.local: Name or service not known

  • Cause: Your host machine cannot translate the mDNS .local address to an IP. This is a host-side or router-side issue, not a Pi issue.
  • Fix: First, verify your host PC has an mDNS resolver (macOS and Linux have this built-in; Windows requires the Bonjour Print Services or the 'Bonjour' feature enabled in modern Windows 11 builds). Second, log into your router's admin panel and check the DHCP client list to find the Pi's actual IP address, then SSH using the raw IP (e.g., ssh myuser@192.168.1.50).

Automated SSH Watchdog & GPIO Fallback Script

In remote or embedded deployments (like a greenhouse monitor or a remote weather station), the SSH daemon can occasionally hang due to memory leaks or network stack crashes. Reaching the physical 'reboot' button isn't always possible. We can build a hardware watchdog using the Pi's GPIO header that monitors the SSH service and provides a physical button to force-restart it.

GPIO Pin Mapping Table

This script targets the Raspberry Pi 5 (8GB). Wire the following components to the 40-pin header:

Component GPIO Pin (BCM) Physical Pin Wiring Notes
Status LED (Anode) GPIO 17 Pin 11 Use a 220Ω current-limiting resistor in series.
Status LED (Cathode) GND Pin 9 Connect to any ground pin.
Push Button (Signal) GPIO 27 Pin 13 Internal pull-up enabled in code; no external resistor needed.
Push Button (Common) GND Pin 14 Pressing the button pulls GPIO 27 to ground.

Python Watchdog Code

This script uses the gpiozero library (pre-installed on Bookworm) and the subprocess module to query systemd. Save this as ssh_watchdog.py and run it as a background service.

import subprocess
import time
from gpiozero import LED, Button
from signal import pause
import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

# Pin definitions (BCM numbering)
STATUS_LED = LED(17) 
RESTART_BTN = Button(27, pull_up=True, bounce_time=0.5) 

def check_ssh_status():
    """Checks if the sshd service is active via systemctl."""
    try:
        result = subprocess.run(
            ['systemctl', 'is-active', 'ssh'], 
            capture_output=True, 
            text=True,
            timeout=5
        )
        return result.stdout.strip() == 'active'
    except subprocess.TimeoutExpired:
        logging.error('systemctl timed out while checking SSH status.')
        return False
    except Exception as e:
        logging.error(f'Error checking SSH status: {e}')
        return False

def restart_ssh():
    """Force-restarts the SSH daemon when the physical button is pressed."""
    logging.warning('Hardware button pressed! Restarting SSH daemon...')
    STATUS_LED.blink(0.2, 0.2) # Rapid blink during restart
    try:
        subprocess.run(['sudo', 'systemctl', 'restart', 'ssh'], check=True, timeout=10)
        time.sleep(2) # Allow daemon to initialize
        if check_ssh_status():
            logging.info('SSH restarted successfully.')
            STATUS_LED.on()
        else:
            logging.error('SSH failed to restart. Check sshd_config for syntax errors.')
            STATUS_LED.blink(1, 1) # Slow blink indicates failure
    except subprocess.CalledProcessError as e:
        logging.error(f'systemctl failed to restart SSH: {e}')
        STATUS_LED.off()
    except Exception as e:
        logging.error(f'Unexpected error during restart: {e}')
        STATUS_LED.off()

def monitor_loop():
    """Continuous background loop to update LED based on SSH status."""
    while True:
        if check_ssh_status():
            if not STATUS_LED.is_lit or STATUS_LED.pin.state != 1:
                STATUS_LED.on()
        else:
            STATUS_LED.off()
        time.sleep(10) # Poll every 10 seconds

# Bind button event
RESTART_BTN.when_pressed = restart_ssh

# Initial state check
logging.info('SSH Watchdog initialized.')
if check_ssh_status():
    STATUS_LED.on()
else:
    STATUS_LED.off()

try:
    # Run the monitoring loop (blocks indefinitely)
    monitor_loop()
except KeyboardInterrupt:
    logging.info('Watchdog terminated by user.')
    STATUS_LED.off()
Sudo Permissions: For the button restart to work without prompting for a password, you must add your user to the sudoers file for that specific command. Run sudo visudo and add: yourusername ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart ssh

Extending and Simplifying Your Remote Setup

Once you have basic SSH access working, relying on raw IP addresses and passwords becomes a liability. Here is how to simplify your workflow and extend your access beyond the local network.

1. Implement SSH Key Authentication (Simplify)

Stop typing passwords. On your host machine, generate an Ed25519 key pair if you haven't already:

ssh-keygen -t ed25519 -C 'pi5-headless'

Then, push the public key to your Pi:

ssh-copy-id -i ~/.ssh/id_ed25519.pub myuser@raspberrypi.local

Once verified, edit /etc/ssh/sshd_config on the Pi, set PasswordAuthentication no, and restart the daemon. This eliminates brute-force vulnerability and speeds up your login.

2. Configure an SSH Alias (Simplify)

Instead of typing ssh myuser@192.168.1.50 -p 22, create a config file on your host machine at ~/.ssh/config:

Host pi5
    HostName 192.168.1.50
    User myuser
    Port 22
    IdentityFile ~/.ssh/id_ed25519

Now, you simply type ssh pi5 to connect instantly.

3. Extend Access via Tailscale (Extend)

If your Pi is deployed in the field or behind a strict CGNAT (Carrier-Grade NAT) router, port forwarding is a security risk. Instead, install Tailscale. It creates a secure, peer-to-peer WireGuard mesh network. Once installed on both your Pi and your laptop, you can SSH into your Pi using its Tailscale IP (e.g., 100.x.y.z) from anywhere in the world, without opening a single port on your home router.

Mastering SSH on the Raspberry Pi 5 requires respecting the architectural shifts in Bookworm. By utilizing the correct boot partition paths, leveraging NetworkManager, and deploying hardware-level fallbacks, you ensure your embedded projects remain accessible and resilient, even when the network stack misbehaves.