Headless Setup and Remote Management on Raspberry Pi 5
Running Pi's headless — no monitor, no keyboard — is the right approach when they're deployed in the field or tucked away in an enclosure. This covers the full setup: pre-imaging configuration, SSH hardening, keeping the system updated remotely, and the tools that make managing several Pi's at once manageable.
Pre-imaging with Raspberry Pi Imager
Raspberry Pi Imager's advanced options (the gear icon, or Ctrl+Shift+X) are the cleanest way to configure a headless Pi before the first boot. Set these before writing:
| Setting | What to set | Why |
|---|---|---|
| Hostname | Something specific (e.g., pizero-north) | Avoids hostname collisions on the network; easier to find with mDNS |
| SSH | Enable with public key | Paste your ~/.ssh/id_ed25519.pub — no password needed |
| Username/password | Set a non-default user | Default "pi" is a known target |
| WiFi | SSID + password (if using wifi) | Written to /etc/wpa_supplicant/wpa_supplicant.conf on boot |
| Locale/timezone | Set correctly | Correct timestamps in logs; RTC accuracy |
Finding the Pi on the Network
# mDNS — works if your router/network supports it (most do)
$ ssh pi@hostname.local
# Scan the local subnet for new hosts
$ nmap -sn 192.168.1.0/24
# Check your router's DHCP lease table (fastest if you have router access)
# Set a static IP — edit /etc/dhcpcd.conf or use NetworkManager:
$ sudo nmcli con mod "Wired connection 1" \
ipv4.addresses 192.168.1.50/24 \
ipv4.gateway 192.168.1.1 \
ipv4.dns "192.168.1.1" \
ipv4.method manual
$ sudo nmcli con up "Wired connection 1"
SSH Key Setup
# On your local machine — generate a key if you don't have one $ ssh-keygen -t ed25519 -C "jason@mymachine" # Copy it to the Pi (if not done at imaging time) $ ssh-copy-id -i ~/.ssh/id_ed25519.pub pi@raspberrypi.local # Test key auth works, then disable password auth on the Pi: # Edit /etc/ssh/sshd_config: PasswordAuthentication no PermitRootLogin no MaxAuthTries 3 $ sudo systemctl reload sshd
SSH Client Config (~/.ssh/config)
With multiple Pi's, a client config saves a lot of typing:
Host pi5-home
HostName 192.168.1.50
User pi
IdentityFile ~/.ssh/id_ed25519
Host pico-gateway
HostName 192.168.1.51
User pi
IdentityFile ~/.ssh/id_ed25519
Host vim3
HostName 192.168.1.52
User jason
IdentityFile ~/.ssh/id_ed25519
# Now: ssh pi5-home (instead of ssh pi@192.168.1.50)
Keeping the System Updated
# Manual update $ sudo apt update && sudo apt full-upgrade -y # Automated unattended upgrades (security patches only — safe for headless) $ sudo apt install unattended-upgrades $ sudo dpkg-reconfigure unattended-upgrades # select "Yes" # Check what would be upgraded $ sudo unattended-upgrade --dry-run --debug
Monitoring System Health Remotely
# Temperature and throttling
$ vcgencmd measure_temp
$ vcgencmd get_throttled # 0x0 = healthy
# CPU frequency (throttled = lower)
$ vcgencmd measure_clock arm
# Memory
$ free -h
# Disk
$ df -h
# One-liner health check — useful in a quick alias
$ echo "Temp: $(vcgencmd measure_temp) | Throttle: $(vcgencmd get_throttled) | Mem: $(free -h | awk 'NR==2{print $3"/"$2}')"
tmux for Persistent Sessions
When running something long over SSH, tmux keeps the session alive if your connection drops:
# Install $ sudo apt install tmux # Basic usage $ tmux new -s mysession # start a named session $ tmux attach -t mysession # reattach after reconnect # Inside tmux: Ctrl+B D # detach (session keeps running) Ctrl+B C # new window Ctrl+B N # next window Ctrl+B ? # help / key bindings # List sessions $ tmux ls
Managing Multiple Pi's: GNU Parallel / pssh
# Run a command on several Pi's at once
# Using a simple loop:
for host in pi5-home pico-gateway vim3; do
ssh $host "sudo apt update && sudo apt -y upgrade" &
done
wait
# Or with pssh (parallel-ssh):
$ sudo apt install pssh
$ pssh -H "pi5-home pico-gateway vim3" -l pi "vcgencmd measure_temp"
Sending Files
# Copy a file to a Pi $ scp ./myscript.py pi5-home:~/scripts/ # Copy a directory $ scp -r ./project/ pi5-home:~/ # rsync — faster for subsequent syncs (only transfers changes) $ rsync -avz ./project/ pi5-home:~/project/ # Pull logs back from the Pi $ rsync -avz pi5-home:/var/log/myapp/ ./logs/
dispelled