Skip to content

Instantly share code, notes, and snippets.

@paalbra
Last active July 20, 2025 12:33
Show Gist options
  • Select an option

  • Save paalbra/f623b9d55007b2f413f072ef5644d5db to your computer and use it in GitHub Desktop.

Select an option

Save paalbra/f623b9d55007b2f413f072ef5644d5db to your computer and use it in GitHub Desktop.
Initialization script for Raspberry PI (first boot)
#!/bin/bash
WLAN_COUNTRY="NO"
LOCALE_KEYMAP="no"
LOCALE_TIMEZONE="Europe/Oslo"
if [ "$EUID" -ne 0 ]; then
>&2 echo "Error. Need to be root."
exit 1
fi
if [ -z $1 ] || [ -z "$2" ]; then
>&2 echo "Usage: $0 DEVICE ISOFILE"
exit 1
fi
DEVICE="$1"
ISOFILE="$2"
if [ ! -b "$DEVICE" ] || [ ! -f "$ISOFILE" ]; then
>&2 echo "Error. No such device \"$DEVICE\" or file \"$ISOFILE\"."
exit 1
fi
readarray -t DEVICEPARTS < <(lsblk -ln -o PATH "$DEVICE")
for i in "${!DEVICEPARTS[@]}"; do
if findmnt -rnS "${DEVICEPARTS[$i]}" >/dev/null; then
>&2 echo "Error. Parition is mounted \"${DEVICEPARTS[$i]}."
exit 1
fi
done
read -p "Password for user \"pi\": " -s PI_PASSWORD && echo
read -p "Wifi SSID: " WIFI_SSID
read -p "Password for wifi: " -s WIFI_PASSWORD && echo
echo "Writing \"$ISOFILE\" to \"$DEVICE\"."
dd if="$ISOFILE" of="$DEVICE" bs=128M status=progress
readarray -t DEVICEPARTS < <(lsblk -ln -o PATH "$DEVICE")
if [ ${#DEVICEPARTS[@]} -ne 3 ]; then
>&2 echo "Error. Device \"$DEVICE\" does not have two partitions: \"${DEVICEPARTS[@]}\"."
exit 1
fi
BOOTPART="${DEVICEPARTS[1]}"
ROOTPART="${DEVICEPARTS[2]}"
echo "Boot partition: $BOOTPART"
echo "Root partition: $ROOTPART"
BOOTMNT="$(mktemp -d /tmp/tmp.rpi.boot.XXXXXXXXXX)/"
ROOTMNT="$(mktemp -d /tmp/tmp.rpi.root.XXXXXXXXXX)/"
mount -o rw,noatime "$BOOTPART" "$BOOTMNT"
mount -o rw,noatime "$ROOTPART" "$ROOTMNT"
if [ ! -f "${ROOTMNT}etc/rpi-issue" ]; then
>&2 echo "Error. rpi-issue file not found: \"${ROOTMNT}etc/rpi-issue\"."
exit 1
fi
if ! ls "$BOOTMNT"*rpi* &>/dev/null; then
>&2 echo "Error. Boot mount doesn't look like a Raspberry PI boot mount: \"$BOOTMNT\"."
exit 1
fi
cat << EOF > "${BOOTMNT}custom.toml"
config_version = 1
[system]
hostname = "raspy"
[user]
name = "pi"
password = "$(echo "$PI_PASSWORD" | openssl passwd -5 -stdin)"
password_encrypted = true
[ssh]
enabled = true
password_authentication = true
[wlan]
ssid = "$WIFI_SSID"
password = "$WIFI_PASSWORD"
country = "$WLAN_COUNTRY"
[locale]
keymap = "$LOCALE_KEYMAP"
timezone = "$LOCALE_TIMEZONE"
EOF
less "${BOOTMNT}custom.toml"
cat << EOF > "${ROOTMNT}init.sh"
PACKAGES=(
lsof
python3-gpiozero
python3-picamera
python3-venv
vim
)
function header() {
echo -e "\n========================="
date --iso-8601=s
echo -e "=========================\n"
}
function perform() {
header
echo "$1"
shift
echo Performing: "\${@}"
"\$@"
}
function set_hostname() {
if [[ "\$HOSTNAME" != "raspy" ]]; then
echo "Custom hostname already set: \$HOSTNAME"
return
fi
# Notes:
# Example content from /proc/device-tree/model:
# * 1b: Raspberry Pi Model B Rev 2
# * 3b+: Raspberry Pi 3 Model B Plus Rev 1.3
# * 4b: Raspberry Pi 4 Model B Rev 1.1
LASTMAC=\$(perl -pe 's/://g' /sys/class/net/wlan0/address | tail -c 5)
SHORTNAME=\$(perl -pe 's/(Raspberry Pi|Model|Rev .*|Plus| )//g' /proc/device-tree/model | tr '[:upper:]' '[:lower:]')
NEW_HOSTNAME="pi-\$SHORTNAME-\$LASTMAC"
echo "HOSTNAME: \\"\$HOSTNAME\\", NEW_HOSTNAME: \\"\$NEW_HOSTNAME\\""
hostnamectl set-hostname "\$NEW_HOSTNAME" && perl -i -pe "s/raspy/\$NEW_HOSTNAME/g" /etc/hosts
}
function install_and_upgrade() {
apt-get update
apt-get install -y "\$@"
apt-get upgrade -y
}
perform "Setting hostname:" set_hostname
perform "Enabling SSH:" systemctl enable --now ssh.service
perform "Current ENV:" env
perform "Sleeping 30s. Hoping for network..." sleep 30
perform "IP info:" ip address
header
if ! ping -c 4 -W 1 8.8.8.8; then
echo "No network? Exiting"
exit 1
fi
perform "Performing apt-get update, install and upgrade:" install_and_upgrade \${PACKAGES[*]}
perform "Done. Rebooting..." /usr/sbin/reboot
EOF
less "${ROOTMNT}init.sh"
echo "@reboot root /bin/bash /init.sh 1> /init.log 2>&1 && rm /etc/cron.d/init" > "${ROOTMNT}/etc/cron.d/init"
less ${ROOTMNT}etc/cron.d/init
umount "$BOOTMNT"
umount "$ROOTMNT"
rmdir "$BOOTMNT"
rmdir "$ROOTMNT"
echo "Done."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment