Martin Bauer ca0271ba44 Disable the Raspberry Pi first-boot user wizard
Removing the SSH banner only hid the symptom. userconfig.service is a
Type=oneshot unit that runs

    whiptail --inputbox "Which user would you like to rename:" 20 60 pi

on tty8 and waits for an answer, which on a headless Pi never comes - it had
been sitting in "activating" for four and a half hours on musicdolphin. It is
WantedBy=multi-user.target, so that target never finished activating either, and
the boot job for it was still queued from boot. Anything ordered
After=multi-user.target then gets a start job that queues behind it and hangs
forever: musicmouse.service never started, and the ansible task that starts it
blocked until killed.

The pi user already exists on these images, so the wizard has nothing to do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 20:41:07 +02:00
2024-07-28 08:45:01 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00

ansible

Personal Ansible setup for provisioning and maintaining a fleet of Raspberry Pis (audio players, sensors, music mouse, etc.) plus one home server.

Layout

  • inventory.yml — hosts and group vars (Pis under iot, plus server). group_vars/ and host_vars/ hold vars for the mediapis group (the four parallel audio-player Pis) and their host-specific overrides.
  • mediapis.yml, working.yml, server.yml, newrpi-provisioning.yml, octopisetup.yml — top-level playbooks. mediapis.yml is the canonical playbook for the mediapis group (musicdolphin, kitchenpi, bedroompi, musicmouse); the others are narrower/ad-hoc runs kept around for specific hosts or one-off tasks.
  • roles/ — one role per piece of functionality (audio backends, sensors, bluetooth monitoring, server basics, etc.). Each has a short README.md.
  • update-packages.yml — deliberate, fleet-wide package update (see "Keeping packages up to date" below). update-packages-pinned-example.yml is a template for pinning or bumping a single package outside that.
  • lookup_plugins/keepass.py — custom lookup plugin that fetches secrets (device passwords, wifi passphrase) from a running KeePassXC instance via its browser-integration protocol, instead of storing them in the repo.
  • pis/ — loose config files/scripts used when provisioning Pis by hand.
  • scripts/ — standalone helper scripts (Raspbian image creation, a network logger) that aren't Ansible roles.
  • archive/ — retired setups kept for reference (not actively maintained).

Setup

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Secrets are pulled from KeePassXC at run time via the keepass lookup plugin — see the header of lookup_plugins/keepass.py for how to enable Browser Integration in KeePassXC. Freshly-flashed Raspberry Pis are reached first with the OS-default pi/raspberry credentials (see roles/pi_standard_setup), which the role then rotates to a KeePassXC-managed password.

Running a playbook

ansible-playbook mediapis.yml --limit <host>

ansible.cfg points Ansible at inventory.yml and roles/ by default, so no extra flags are needed for those.

Keeping packages up to date

Regular playbook runs (mediapis.yml, server.yml, etc.) use state: present for packages, so they only install what's missing — they never upgrade anything as a side effect of an unrelated config change. Two separate, deliberate mechanisms handle upgrades instead:

Security patches — automatic. The unattended_upgrades role (applied to every host in mediapis.yml/server.yml) configures unattended-upgrades to install security-origin updates automatically, with a scheduled reboot window (default 03:00, see roles/unattended_upgrades/defaults/main.yml) for patches that need one. Not scoped to full dist-upgrades.

Everything else — deliberate, ad hoc.

just update <host_or_group>          # e.g. `just update kitchenpi` or `just update`
venv/bin/ansible-playbook update-packages.yml --limit <host> --check --diff   # dry run first

Defaults to upgrade: safe (upgrades in place, never installs/removes packages to resolve dependencies — see the comment header in update-packages.yml for the tradeoff against dist). There is no cron/ schedule wired up for this yet; run it ad hoc when you want the fleet updated, or add a crontab entry yourself (a starting point is documented in update-packages.yml's header).

To pin a package to an exact version, or deliberately bump one named package to latest outside this schedule, copy the pattern in update-packages-pinned-example.yml.

Description
No description provided
Readme 85 MiB
Languages
Jupyter Notebook 79.8%
Python 13.3%
Jinja 3.2%
C++ 1.7%
Shell 1.3%
Other 0.6%