To enable SSH Raspberry Pi on modern Bookworm or Trixie OS without a monitor, you must place an empty file named ssh (no extension) and a userconf.txt file containing a hashed password into the boot/firmware partition before first boot. This bypasses the default security lockdown introduced in recent OS releases, allowing immediate remote headless access for embedded deployments.
raspi-config GUI method is useless when your Pi 5 is buried in a control panel or your Zero 2 W is soldered into a custom PCB. Headless provisioning via the boot partition is the industry standard for fleet deployment.
1. The Headless Embedded Workflow: Parts & Pinout
Before writing a single line of code, ensure your hardware baseline is stable. The Raspberry Pi 5 is highly sensitive to power delivery; a brownout will silently disable the USB-C controller and Ethernet PHY, making SSH impossible even if configured perfectly.
| Component | Exact Variant / Spec | Why It Matters for SSH |
|---|---|---|
| Compute Board | Raspberry Pi 5 (8GB) or Pi Zero 2 W | Target boards for this guide; Pi 5 requires 27W PD for full peripheral uptime. |
| Power Supply | 27W USB-C PD (5V/5A) | Prevents brownout-induced network drops during SSH handshake. |
| Storage | 32GB A2-Rated MicroSD or NVMe SSD | A2 rating ensures the OS generates SSH host keys fast enough on first boot. |
| Debug Fallback | CP2102 or PL2303 USB-to-TTL Serial | Required when network SSH fails; provides direct console access via UART. |
UART Serial Pin Mapping (The Ultimate Fallback)
When network SSH fails, UART is your lifeline. Wire your CP2102 adapter to the Pi's primary UART0 pins. Note: On Pi 5, UART0 is mapped to GPIO 14/15 by default, but verify your config.txt if using custom overlays.
| Pi GPIO Pin | Physical Pin # | Function | CP2102 Adapter Wire |
|---|---|---|---|
| GND | 6 | Ground Reference | GND |
| GPIO 14 (TXD) | 8 | Transmit Data | RXD (Receive) |
| GPIO 15 (RXD) | 10 | Receive Data | TXD (Transmit) |
2. Provisioning: Steps to Enable SSH Headless
This procedure targets the Raspberry Pi 5 (8GB) and Pi Zero 2 W running Raspberry Pi OS Bookworm or newer. We use the boot/firmware partition, which is FAT32 formatted and readable by Windows, macOS, and Linux immediately after flashing.
- Flash the OS: Use Raspberry Pi Imager to write the 64-bit Bookworm Lite image to your storage media.
- Mount the Boot Partition: Eject and re-insert the SD card. Open the drive named
bootorbootfs(now technicallyboot/firmwareon the Pi itself, but appears asbooton your host PC). - Create the SSH Flag: Create an empty file named exactly
ssh.Warning: Windows Notepad will silently append.txt. The file must besshwith zero extensions. Use a code editor like VS Code or enable 'File name extensions' in Windows Explorer to verify. - Create the User Configuration: Create a file named
userconf.txt. The defaultpiuser no longer exists. You must define a custom username and an encrypted password. - Generate the Hash: Open a terminal on your host machine (or use an online OpenSSL tool) and run:
openssl passwd -6Enter your desired password. Copy the resulting hash string. - Format userconf.txt: Open
userconf.txtand add a single line:username:encrypted_password_hash(e.g.,fluxuser:$6$xyz...). - Boot and Wait: Insert the SD card into the Pi and apply power. Wait exactly 90 seconds. The Pi must generate unique SSH host keys on first boot; attempting to connect before this finishes will result in a refused connection.
3. Automated Hardware Verification over SSH
Once SSH is active, you need to verify that the OS has correctly mapped the GPIO hardware. The legacy RPi.GPIO library is deprecated and broken on Pi 5 / Bookworm. Modern embedded Python relies on libgpiod via the rpi-lgpio wrapper.
Run sudo apt install python3-rpi-lgpio on your Pi, then execute this script to flash a hardware heartbeat LED on GPIO 17. This confirms SSH access, Python environment health, and hardware pinmuxing simultaneously.
import lgpio
import time
import sys
# Pin definition for hardware heartbeat verification
LED_PIN = 17
CHIP = 0 # Default GPIO chip for Pi 4/5
def setup_gpio():
"""Claims GPIO 17 as an output. Fails gracefully if pin is busy."""
try:
h = lgpio.gpiochip_open(CHIP)
lgpio.gpio_claim_output(h, LED_PIN)
print(f"Successfully claimed GPIO {LED_PIN} on chip {CHIP}")
return h
except lgpio.error as e:
print(f"[FATAL] GPIO Claim Failed: {e}")
print("Check if another process (e.g., pigpiod) is hogging the pin.")
sys.exit(1)
def heartbeat(handle):
"""Blinks LED 5 times to visually confirm remote SSH execution."""
try:
for i in range(5):
lgpio.gpio_write(handle, LED_PIN, 1)
time.sleep(0.5)
lgpio.gpio_write(handle, LED_PIN, 0)
time.sleep(0.5)
print("Heartbeat sequence complete. Hardware verified.")
except Exception as e:
print(f"[ERROR] Execution interrupted: {e}")
finally:
# Always release the chip to prevent pin lockouts
lgpio.gpiochip_close(handle)
if __name__ == "__main__":
print("SSH Connection Verified. Starting Hardware Heartbeat.")
chip_handle = setup_gpio()
heartbeat(chip_handle)
4. Debugging Connection Failures: Exact Errors & Fixes
When headless setups fail, they fail silently. Here are the exact error strings you will see, ranked by probability, and how to fix them.
Error 1: ssh: connect to host raspberrypi.local port 22: Connection refused
The First Three Things to Check:
- Hidden File Extensions: Your
sshfile is actually namedssh.txt. The OS ignored it. Mount the SD card on your PC, enable hidden extensions, and rename it. - Host Key Generation Delay: You tried to connect at 45 seconds. On a Pi Zero 2 W, generating RSA/Ed25519 host keys can take up to 120 seconds on a slow SD card. Wait 3 minutes and try again.
- mDNS / Avahi Failure: Your router blocks multicast DNS, so
raspberrypi.localwon't resolve. Log into your router's DHCP table, find the Pi's IP address (e.g.,192.168.1.45), and SSH directly to the IP.
Error 2: Permission denied (publickey,password)
- Malformed Password Hash: You pasted the raw password into
userconf.txtinstead of the OpenSSL hash, or you missed theusername:prefix. The OS rejected the file and locked SSH. - Root Login Attempt: You typed
ssh root@raspberrypi.local. Root SSH is disabled by default in Bookworm. Use the custom username you defined inuserconf.txt. - Keyboard Layout Mismatch: If you generated the password hash on a UK keyboard but typed it on a US keyboard during SSH, special characters (like
#or@) will mismatch. Stick to alphanumeric passwords for initial provisioning.
5. Frequently Asked Questions
How to enable SSH Raspberry Pi headless without a monitor?
Place an empty file named ssh and a userconf.txt file (containing username:hashed_password) in the root directory of the FAT32 boot partition on your SD card before inserting it into the Pi. Upon first boot, the OS moves these files to the secure partition and starts the sshd daemon automatically.
Why is my Raspberry Pi SSH connection refused on first boot?
The most common cause is interrupting the boot sequence before SSH host keys are generated. If the Pi loses power during the 60-120 second key generation window, the SSH service will fail to start on subsequent boots to prevent security vulnerabilities. Reflash the OS and ensure stable 27W power delivery during the first boot.
How to enable SSH Raspberry Pi via UART serial console?
If network SSH is completely dead, connect a USB-to-TTL adapter to GPIO 14 (TXD), GPIO 15 (RXD), and GND. Open a serial terminal (like PuTTY or screen /dev/ttyUSB0 115200) at 115200 baud. Once logged in via serial, run sudo raspi-config, navigate to Interface Options > SSH, and enable it manually, then run sudo systemctl restart ssh.
How to extend or simplify the build for fleet deployment?
To Simplify: Use the Raspberry Pi Imager GUI on your host PC. Click the 'Gear' icon (OS Customization) to inject your SSH credentials, Wi-Fi SSID, and hostname directly into the image before flashing. This eliminates manual file creation.
To Extend: For production embedded systems, disable password authentication entirely. Generate an Ed25519 keypair on your host (ssh-keygen -t ed25519), and use Pi Imager to inject your .pub key into the Pi's authorized_keys. Then, edit /etc/ssh/sshd_config via your automation script to set PasswordAuthentication no, securing the device against brute-force attacks on the factory floor.






