The Quick Fix: Why Your SSH Login to Raspberry Pi is Failing

If your terminal returns ssh: connect to host [IP] port 22: Connection refused when attempting an SSH login to Raspberry Pi hardware, the SSH daemon is either disabled or blocked. In modern Raspberry Pi OS (Bookworm and later), SSH is disabled by default for security. The immediate fix is to place an empty file named ssh (no extension) in the /boot/firmware/ partition of your microSD or NVMe drive before booting, or enable it via sudo raspi-config if you have a monitor attached.

For embedded engineers running headless IoT nodes or robotics controllers, a failed SSH connection halts the entire deployment. This guide cuts through outdated tutorials, targets the Raspberry Pi 5 (8GB) running Raspberry Pi OS Bookworm 64-bit, and provides a hardware-verified debugging workflow using UART fallbacks and GPIO status indicators.

Decision Tree: Choosing Your Headless Access Method

Before writing a single line of code or crimping a connector, decide how you will authenticate. Password authentication is a security liability on any network you don't strictly control. Use this decision matrix to select your method.

Auth MethodSecurity LevelSetup EffortNetwork Required?Verdict
Password AuthLowLowYesAvoid for exposed networks
Ed25519 SSH KeysHighMediumYesDEFAULT PICK
USB-C OTG GadgetHighHighNo (Direct)Use only for isolated field debugging
Concrete Pick: Always default to Ed25519 SSH Keys over local Wi-Fi or Ethernet. It eliminates brute-force vulnerabilities and allows seamless automated scripting via tools like Ansible or Fabric without hardcoding passwords in your deployment scripts.

Parts List & UART Debug Pin Mapping

When Wi-Fi drops or DHCP fails, SSH is useless. Every serious headless build requires a physical UART debug header. Here is the exact bill of materials and pinout for a robust Raspberry Pi 5 debugging station.

Hardware BOM

  • Compute: Raspberry Pi 5 (8GB variant) with active cooler
  • Storage: 64GB NVMe SSD via official PCIe Gen 2 HAT (abandon microSD for production)
  • Power: 27W USB-C PD Power Supply (Pi 5 requires PD for full 5A/5V delivery)
  • Debug: CP2102 or FT232RL USB-to-TTL Serial Cable (3.3V logic only!)
  • Indicator: 5mm Red LED + 330Ω through-hole resistor + breadboard

Pin Mapping Table

The Raspberry Pi 5 maintains the standard 40-pin header layout, but be aware that the UART debugging requires a 3.3V logic-level adapter. Never use a 5V RS-232 adapter; you will fry the SoC.

FunctionBCM GPIOPhysical PinWiring Notes
UART TX148Connect to USB-TTL adapter RX
UART RX1510Connect to USB-TTL adapter TX
UART GNDN/A6Common ground required for signal reference
Status LED1711Anode to Pin 11, Cathode via 330Ω to Pin 9 (GND)

Step-by-Step: Enabling SSH and Generating Ed25519 Keys

Follow these exact terminal commands on your host machine (Linux/macOS/Windows 11) to establish a secure, passwordless SSH login to Raspberry Pi.

  1. Generate the Key Pair: On your host machine, open your terminal and run:
    ssh-keygen -t ed25519 -C "pi5-bench-deploy"
    Press Enter to accept the default path (~/.ssh/id_ed25519). Do not add a passphrase if you intend to use this key in automated CI/CD pipelines.
  2. Push the Key to the Pi: Assuming your Pi is on the network and SSH is enabled, copy the public key:
    ssh-copy-id -i ~/.ssh/id_ed25519.pub username@192.168.1.50
    Note: Replace 'username' with the custom user you created in Raspberry Pi Imager. The default 'pi' user no longer exists in Bookworm.
  3. Disable Password Authentication: SSH into the Pi one last time using the key, edit the daemon config:
    sudo nano /etc/ssh/sshd_config
    Find and change PasswordAuthentication yes to PasswordAuthentication no.
  4. Restart the Daemon: Apply changes with sudo systemctl restart ssh.

Troubleshooting Exact SSH Error Strings

When your SSH login to Raspberry Pi fails, the terminal spits back a specific string. Here are the exact errors, ranked by probability, and how to fix them.

Error 1: ssh: connect to host 192.168.1.50 port 22: Connection refused

What it means: The Pi is online and reachable via ICMP (ping), but port 22 is actively rejecting traffic.

  • Cause 1 (Most Likely): SSH is disabled. Fix: Power down, mount the drive on your PC, and create an empty file named ssh in the /boot/firmware/ directory. (Note: In older Bullseye releases, this was just /boot/).
  • Cause 2: IP address changed via DHCP. Fix: Check your router's ARP table or use ping raspberrypi.local via mDNS.
  • Cause 3: iptables or ufw is blocking port 22. Fix: Connect via the UART debug header (pins 6, 8, 10 at 115200 baud) and run sudo ufw allow 22/tcp.

Error 2: username@192.168.1.50: Permission denied (publickey,password)

What it means: The server is accepting connections, but your credentials are rejected.

  • Cause 1 (Most Likely): You are trying to log in as pi. Fix: Modern Raspberry Pi OS images force you to create a custom user. Use that custom username.
  • Cause 2: Incorrect permissions on the Pi's .ssh folder. Fix: Via UART, run chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys. SSHD will silently reject keys if the directory is world-readable.

Error 3: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!

What it means: The cryptographic fingerprint of the target IP doesn't match your host's known_hosts file.

  • Cause 1 (Most Likely): You wiped and reimaged the Pi's NVMe/microSD card. Fix: Run ssh-keygen -R 192.168.1.50 on your host machine to clear the old fingerprint.
  • Cause 2: A Man-in-the-Middle (MITM) attack or IP collision. Fix: Verify the MAC address on your router to ensure another device hasn't claimed the Pi's old IP.

Python GPIO Verification Script (Target: Pi 5 / Bookworm)

Once your SSH login to Raspberry Pi succeeds, you need to verify that hardware access works over the remote session. The following Python script uses the gpiozero library (pre-installed on Bookworm) to blink the status LED on GPIO 17. This confirms both the SSH session and the SoC's GPIO multiplexer are functioning.

Target Board: Raspberry Pi 5 (8GB) / Raspberry Pi OS Bookworm 64-bit.

#!/usr/bin/env python3
"""
SSH Hardware Verification Script
Blinks an LED on GPIO 17 to confirm remote hardware access.
"""
import sys
import time
from gpiozero import LED
from signal import pause

# Pin Definition: BCM GPIO 17 (Physical Pin 11)
# Ensure a 330 ohm resistor is in series with the LED anode.
STATUS_LED = LED(17)

def run_hardware_test():
    try:
        print("[INFO] SSH Hardware Verification: Blinking GPIO 17 LED...")
        # Blink 5 times, blocking execution until complete
        STATUS_LED.blink(on_time=0.5, off_time=0.5, n=5, background=False)
        print("[PASS] Hardware test passed. GPIO multiplexer responsive.")
        
    except Exception as e:
        print(f"[FAIL] Hardware Error: {e}", file=sys.stderr)
        sys.exit(1)
        
    finally:
        # Ensure the pin is driven low on exit to prevent ghost voltage
        STATUS_LED.off()
        print("[INFO] GPIO 17 cleaned up and set to LOW.")

if __name__ == "__main__":
    run_hardware_test()
First Three Things to Check When the Script Fails:
  1. Is the user in the correct group? Run groups. If you don't see gpio or dialout, your custom user lacks hardware permissions. Fix with sudo usermod -aG gpio $USER and reboot.
  2. Is the PWM/GPIO firmware loaded? Pi 5 uses the RP1 southbridge chip. Ensure your kernel is updated via sudo apt update && sudo apt full-upgrade to pull the latest RP1 drivers.
  3. Is the physical circuit complete? Use a multimeter in continuity mode to verify the breadboard ground rail is actually tied to Physical Pin 9.

Extending or Simplifying the Build

You now have a secure, hardware-verified SSH pipeline. Depending on your deployment environment, you should adjust the complexity.

How to Simplify (For Desktop Prototyping)

If you are just prototyping on a desk and don't need production-grade security, skip the terminal key generation entirely. Open the Raspberry Pi Imager software on your PC, select your OS, click the OS Customisation (gear icon), and check 'Enable SSH'. Select 'Use password authentication' and type your desired password. The Imager injects the configuration directly into the /boot/firmware/ partition before you even insert the drive into the Pi.

How to Extend (For Rackmount / IoT Deployments)

For remote field deployments where physical access is impossible, extend this build by adding a hardware watchdog and an SSH-login physical alert. 1. Wire an active piezo buzzer to GPIO 22. 2. Configure Linux PAM (Pluggable Authentication Modules) to execute a script upon successful SSH login. 3. Edit /etc/pam.d/sshd and add:
session optional pam_exec.so /usr/local/bin/ssh_alert.sh
This triggers the buzzer for 2 seconds every time a remote user authenticates, providing an immediate auditory cue in a server rack that someone has just accessed the node.