Using cloud-init to produce bootable ssh-ready Raspberry Pi SD cards
Raspberry Pi OS Trixie images released on November 24, 2025 and later support cloud-init, making it easy to configure bootable SD cards for your personal needs.
This is super convenient but it comes with a security trade-off:
network-config and user-data live on the SD card's FAT boot partition, so
anyone with access to the card or the machine used to prepare it can read them
easily.
Here I demonstrate how to use cloud-init to set up Wi-Fi and SSH, so that your
Pi is ready to use remotely without having to connect it to a monitor and
keyboard first. Because the boot partition remains mounted at
/boot/firmware after boot, these files are still present on the running Pi.
Treat any secrets placed there, especially Wi-Fi credentials, as exposed.
This example also grants passwordless sudo to the initial user so that we do not need to store a local password hash on the boot partition. That is another trade-off: if this user account is compromised, root access is immediate. For a personal Pi on a trusted network that may be acceptable, but on shared or Internet-exposed systems you should revisit this after first boot.
After flashing the Pi OS image, copy the following to /network-config on the
boot filesystem:
# The following config sets up Wi-Fi and/or ethernet access
network:
# See https://netplan.readthedocs.io/en/latest/netplan-yaml/
version: 2
# NetworkManager is the default network manager on Raspberry Pi OS
renderer: NetworkManager
wifis:
wlan0:
# Resolve IP dynamically via DHCP
dhcp4: true
# Set the Wi-Fi country code for legal channel/power limits
regulatory-domain: "US"
access-points:
# SSID
"<SSID>":
# password
password: "<WIFI-PASSWORD>"
ethernets:
eth0:
# Resolve IP dynamically via DHCP
dhcp4: true
and the following to /user-data, also on the boot filesystem:
#cloud-config
# The line above is a required magic header
# Hostname, but to actually access this locally, we need to set up mDNS (see
# below) and connect via <HOSTNAME>.local
hostname: <HOSTNAME>
# Updates /etc/hosts to match the hostname
manage_etc_hosts: true
users:
# name is the username that we use to ssh
- name: <USERNAME>
# display name
gecos: <DISPLAY-NAME>
# login shell
shell: /bin/bash
# public keys allowed to SSH in
ssh_authorized_keys:
- ssh-ed25519 <SSH-PUBLIC-KEY> <EMAIL>
# Allow this user to run any command via sudo without a password prompt
sudo: ALL=(ALL) NOPASSWD:ALL
# Lock the account password so login is SSH-key-only
lock_passwd: true
# `false` disables SSH password authentication, so logins are key-only
ssh_pwauth: false
# enables the SSH server
enable_ssh: true
packages:
# Sets up mDNS so we can connect via <HOSTNAME>.local
- avahi-daemon
I'll explore more secure alternatives in a future post.
If you enjoyed this post, please let me know on Twitter or Bluesky.
Posted April 12, 2026.
Tags: #linux