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:

SettingWhat to setWhy
HostnameSomething specific (e.g., pizero-north)Avoids hostname collisions on the network; easier to find with mDNS
SSHEnable with public keyPaste your ~/.ssh/id_ed25519.pub — no password needed
Username/passwordSet a non-default userDefault "pi" is a known target
WiFiSSID + password (if using wifi)Written to /etc/wpa_supplicant/wpa_supplicant.conf on boot
Locale/timezoneSet correctlyCorrect 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/

References