To add an SSH key to a Raspberry Pi, you must append your public key (usually id_ed25519.pub or id_rsa.pub) to the ~/.ssh/authorized_keys file on the target device. For headless deployments, the most reliable method is injecting the key during the OS flashing process via the Raspberry Pi Imager, or placing it in the boot partition before the first boot. This eliminates the need for a monitor and keyboard while securing the device against brute-force password attacks.

When deploying fleets of Raspberry Pi 5 or Pi Zero 2 W nodes in remote enclosures, passwordless SSH is mandatory. But what happens when a network glitch or a corrupted key file locks you out of a headless node? Below, we cover the exact software injection methods, a hardware GPIO failsafe to recover locked nodes, and the specific error strings you will encounter when things go wrong.

Hardware & Software Bill of Materials (BOM)

This guide targets the Raspberry Pi 5 (8GB variant) and the Raspberry Pi Zero 2 W running Raspberry Pi OS Bookworm (64-bit). The hardware failsafe circuit requires minimal components.

  • Compute: Raspberry Pi 5 8GB (SC11120) OR Raspberry Pi Zero 2 W
  • Storage: SanDisk Extreme 32GB microSD (A2 rating for Bookworm's heavier I/O)
  • Indicator: 5mm Green LED + 330Ω through-hole resistor
  • Input: 12mm tactile momentary pushbutton
  • Wire: 22 AWG solid core hookup wire (4 colors)
Difficulty Rating: Intermediate (Hardware) / Beginner (Software)
Time Required: 45 minutes (including hardware assembly and code testing)

Step-by-Step: Injecting the SSH Key

There are two primary ways to inject your key. Method 1 is for new deployments; Method 2 is for existing nodes.

Method 1: Pre-Boot Injection via Raspberry Pi Imager

  1. Open Raspberry Pi Imager on your host machine and select your OS (Bookworm 64-bit).
  2. Select your target storage (microSD card).
  3. Click the Advanced Options gear icon (or press Ctrl+Shift+X).
  4. Check Enable SSH and select Use public-key authentication.
  5. Paste your public key into the text box. (Find it on your host via cat ~/.ssh/id_ed25519.pub).
  6. Flash the card. The Imager automatically creates the .ssh directory and authorized_keys file in the user's home directory on the rootfs partition.

Method 2: Post-Boot Injection via ssh-copy-id

If the Pi is already on the network and accepts passwords, push the key from your host terminal:

ssh-copy-id -i ~/.ssh/id_ed25519.pub username@raspberrypi.local
Table 1: OS Partition Paths for Manual Key Injection
OS VersionBoot Partition PathSSH Enablement File
Bookworm (Current)/boot/firmware/Create empty file named ssh or ssh.txt
Bullseye (Legacy)/boot/Create empty file named ssh or ssh.txt

Building a Hardware SSH Failsafe (GPIO Integration)

In remote IoT enclosures, if your SSH key gets overwritten or the sshd config breaks, you are locked out. We can build a physical failsafe: an LED that blinks when an active SSH session is detected, and a button that, when held for 3 seconds, restores a backup authorized_keys file and restarts the SSH daemon.

Pin Mapping Table

ComponentPi GPIO PinPhysical Pin #Wiring Notes
Green LED (Anode)GPIO 1711Wire in series with 330Ω resistor
Green LED (Cathode)GND9Common ground
Tactile Button (Leg 1)GPIO 2713Use internal pull-up via code
Tactile Button (Leg 2)GND14Common ground

Python Failsafe Code

This script targets the Pi 5 and Zero 2 W running Bookworm. It uses the gpiozero library. Save this as ssh_watchdog.py and run it via a systemd service.

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

# Pin Definitions
STATUS_LED = LED(17)
FAILSAFE_BTN = Button(27, pull_up=True, bounce_time=0.05)

# Paths
BACKUP_KEYS = '/home/pi/.ssh/authorized_keys.backup'
ACTIVE_KEYS = '/home/pi/.ssh/authorized_keys'

def check_ssh_sessions():
    """Checks for active SSH connections using the ss command."""
    try:
        # ss command filters for established TCP connections on port 22
        result = subprocess.run(
            ['ss', '-tn', 'state', 'established', '( dport = :22 or sport = :22 )'],
            capture_output=True, text=True, check=True
        )
        # If output has more than 1 line, a session is active
        lines = result.stdout.strip().split('\n')
        return len(lines) > 1
    except subprocess.CalledProcessError as e:
        print(f"Error checking SSH sessions: {e}")
        return False

def restore_keys_and_restart():
    """Restores backup keys and restarts sshd."""
    STATUS_LED.blink(on_time=0.1, off_time=0.1)
    print("Failsafe triggered! Restoring keys...")
    try:
        subprocess.run(['cp', BACKUP_KEYS, ACTIVE_KEYS], check=True)
        subprocess.run(['chmod', '600', ACTIVE_KEYS], check=True)
        subprocess.run(['systemctl', 'restart', 'ssh'], check=True)
        print("SSH service restarted successfully.")
    except subprocess.CalledProcessError as e:
        print(f"Failsafe recovery failed: {e}")
    time.sleep(2)
    STATUS_LED.off()

# Main Loop
if __name__ == '__main__':
    print("SSH Watchdog initialized. Holding button for 3s triggers failsafe.")
    FAILSAFE_BTN.when_held = restore_keys_and_restart
    
    try:
        while True:
            if check_ssh_sessions():
                STATUS_LED.on() # Solid on when SSH is active
            else:
                STATUS_LED.off()
            time.sleep(5)
    except KeyboardInterrupt:
        STATUS_LED.off()
        print("Watchdog terminated.")
Callout Tip: Extending or Simplifying the Build
To simplify, remove the button and LED and just run the check_ssh_sessions() function to log data to a local CSV. To extend, add the paho-mqtt library to publish an alert to your home automation broker (e.g., Home Assistant) if the failsafe button is pressed, notifying you that a remote node was physically accessed.

Debugging SSH Key Failures: Exact Errors & Fixes

When your connection fails, the terminal output tells you exactly what broke. Here are the three things to check first when any SSH key authentication fails:

  1. Permissions: The .ssh directory must be 700 and authorized_keys must be 600. SSH silently refuses keys if permissions are too open.
  2. Daemon Config: Ensure PubkeyAuthentication yes is set and not commented out in /etc/ssh/sshd_config.
  3. Key Type Mismatch: Older Pi OS builds or hardened configs may reject RSA-1024 keys. Always generate and use Ed25519 keys (ssh-keygen -t ed25519).

Error 1: Permission denied (publickey).

Exact String: username@raspberrypi: Permission denied (publickey).
Ranked Causes:

  1. The public key was pasted incorrectly (missing the trailing newline or containing a typo).
  2. The authorized_keys file is owned by root instead of the user (common if you used sudo nano to edit it).
  3. The home directory itself has write permissions for others (e.g., 777). Fix with chmod go-w /home/username.

Error 2: Connection refused

Exact String: ssh: connect to host raspberrypi.local port 22: Connection refused
Ranked Causes:

  1. The SSH daemon is not running. (Fix: plug in a monitor, log in, run sudo systemctl enable --now ssh).
  2. You did not place the empty ssh file in the boot partition during headless setup.
  3. A local firewall (like ufw) is blocking port 22.

Error 3: REMOTE HOST IDENTIFICATION HAS CHANGED!

Exact String: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! ... Offending RSA key in /home/user/.ssh/known_hosts:14
Ranked Causes:

  1. You re-flashed the Pi's SD card, generating new host keys, but your host PC remembers the old ones.
  2. A man-in-the-middle attack (highly unlikely on a local IoT LAN).
  3. Fix: Run ssh-keygen -R raspberrypi.local to clear the old fingerprint from your host's known_hosts file.

Frequently Asked Questions (FAQ)

How do I add an SSH key to Raspberry Pi without a monitor?

The most reliable method is using the Raspberry Pi Imager's Advanced Options menu before flashing the OS. If the Pi is already running headless and you have network access via an Ethernet cable or pre-configured Wi-Fi, you can use the ssh-copy-id command from your host machine, provided you know the default password. If password authentication is disabled and you have no monitor, you must physically remove the SD card, mount it on a Linux PC, and manually append your key to the /home/username/.ssh/authorized_keys file on the rootfs partition.

Why is my Raspberry Pi ignoring my ed25519 SSH key?

If your Pi ignores an Ed25519 key but accepts RSA, you are likely running a severely outdated OS (pre-Buster) or a custom-hardened sshd_config that restricts PubkeyAcceptedKeyTypes. Bookworm fully supports Ed25519 natively. Check your /etc/ssh/sshd_config file to ensure no restrictive Match blocks or PubkeyAcceptedAlgorithms lines are filtering out modern key types. Always verify the key was added to the correct user's directory, not just /root/.ssh/.

Can I add multiple SSH keys to a single Raspberry Pi user?

Yes. The authorized_keys file is simply a plain text list. To add multiple keys for different team members or different host machines, open the file (nano ~/.ssh/authorized_keys) and paste each new public key on a new, separate line. Do not add line breaks within a single key string. The SSH daemon will parse the file line-by-line and grant access to any matching private key presented during the handshake.

How do I disable password authentication after adding my SSH key?

Once you have verified your SSH key works, disabling passwords is critical for security. Open the SSH daemon configuration file: sudo nano /etc/ssh/sshd_config. Find the line #PasswordAuthentication yes, uncomment it by removing the #, and change it to PasswordAuthentication no. Save the file and restart the service with sudo systemctl restart ssh. Warning: Do not close your current active SSH session until you have opened a second terminal window and successfully tested a new connection, otherwise you risk locking yourself out.