Cut the comment bloat from the Podman playbooks

Comment blocks now stay within three lines and explain only the non-obvious. Long histories, include examples, and commented-out code are gone. No task, key, or value changed; the only data change is comment text inside the generated unit files.
This commit is contained in:
tanshu committed 2026-10-18 22:10:57 +05:30
1 parent 2aab1aec1e
commit 476a6d7cd0
85 files changed
+372 -2433

No files matched your search

+1 -7
View File
@@ -1,16 +1,10 @@
---
# All handlers run as the connection user ({{ podman_user }}), so systemctl
# --user works without setting XDG_RUNTIME_DIR.
- name: Reload systemd user units
ansible.builtin.command: systemctl --user daemon-reload
changed_when: true
listen: Reload systemd user units
# A changed environment file or unit file changes the generated unit, so
# Vaultwarden is recreated. The vault itself is in Postgres and the attachments
# are on a bind mount, so a recreate loses nothing — but it is a few seconds of
# downtime for the one site.
# The vault is in Postgres and the attachments are on a bind mount, so a recreate loses nothing.
- name: Restart Bitwarden
ansible.builtin.systemd_service:
name: "{{ unit_name }}"
+8 -53
View File
@@ -1,15 +1,5 @@
---
# Vaultwarden (the Bitwarden-compatible server) as a rootless Podman Quadlet
# unit.
#
# Everything here runs as {{ podman_user }} — the connection user. No become, no
# XDG_RUNTIME_DIR: the SSH connection gave us a real PAM session, so systemd's
# user manager is reachable through the standard environment.
# /data is the image's VOLUME, and it holds everything Vaultwarden owns that is
# not in Postgres: attachments/, sends/, the rsa_key signing pair, the admin
# config and the icon cache. Postgres is authoritative for the vault itself
# (docs/backup-plan.md §3.3).
# /data holds attachments, sends, signing keys, admin config and the icon cache. The vault is in Postgres.
- name: Ensure the Vaultwarden data directory exists
ansible.builtin.file:
path: "{{ data_dir }}"
@@ -18,10 +8,7 @@
group: "{{ podman_user }}"
mode: '0755'
# The app's secrets and database coordinates are consumed by systemd
# (EnvironmentFile= in the Quadlet unit), not by Ansible, so this has to be
# written before the unit starts. Mode 0600: DATABASE_URL carries the Postgres
# password.
# systemd reads this through EnvironmentFile= before the unit starts, and DATABASE_URL carries the Postgres password.
- name: Write the environment file
ansible.builtin.template:
src: bitwarden.env.j2
@@ -51,9 +38,7 @@
state: started
daemon_reload: true
rescue:
# systemd reports "control process exited with error code" and nothing
# else, which is useless for the usual failures (an unattached network, an
# unreachable database, a bad volume). Pull the real reason out.
# systemd only reports "control process exited with error code", so pull the real reason.
- name: Collect Vaultwarden diagnostics
ansible.builtin.command: "{{ item }}"
loop:
@@ -72,35 +57,14 @@
ansible.builtin.fail:
msg: "Vaultwarden did not start — see the diagnostics above."
# Quadlet honours [Install] WantedBy= when it generates the unit, so
# `systemctl --user enable` is neither needed nor possible on a generated unit.
# Quadlet honours [Install] WantedBy=, so `systemctl --user enable` is not needed on a generated unit.
- name: Verify Vaultwarden is running
ansible.builtin.command: "systemctl --user is-active {{ unit_name }}"
register: bitwarden_active
changed_when: false
failed_when: bitwarden_active.stdout != 'active'
# `is-active` says the container is running; it does not say the app can serve.
# Vaultwarden runs its Diesel migrations against Postgres before Rocket starts
# answering, and `Restart=always` means even a crash-looping container is
# reported active.
#
# The probe is the image's own /healthcheck.sh — the script its HEALTHCHECK runs,
# curling http://localhost:80/alive — but it is exec'd directly rather than via
# `podman healthcheck run`. Podman's healthcheck plumbing is unreliable for
# Quadlet-managed rootless containers: the check is registered against the *user*
# systemd manager, and when that fails the check never runs, and `podman
# healthcheck run` reports that the container has no healthcheck at all even
# though the image declares one (upstream issues #26066, #25034, #22804). That is
# exactly what happened on the first deploy here: the app was serving, and the
# probe wrapper was the only thing failing.
#
# Exec'ing the script is the identical probe with no plumbing in between, and it
# still depends on nothing the play has to invent — no curl/wget/python3 of its
# own, just the script the image already ships. The cost is that the path is
# hardcoded; the image's Dockerfile pins it (`COPY docker/healthcheck.sh
# docker/start.sh /`), and if a future tag moves it the failure is a plain 127,
# whose stderr the rescue below prints.
# `is-active` cannot tell whether the app serves, so run the image's own /healthcheck.sh.
- name: Wait for Vaultwarden to answer its own health check
block:
- name: Wait for Vaultwarden to answer its own health check
@@ -111,9 +75,7 @@
delay: 5
changed_when: false
rescue:
# A bare "non-zero return code" hides the one line that matters — curl's
# reason, or "no such file or directory" for a moved script — so pull the
# probe's own output and what the app is saying.
# A bare "non-zero return code" hides curl's reason or a missing script, so print the probe output.
- name: Collect readiness diagnostics
ansible.builtin.command: "{{ item }}"
loop:
@@ -135,9 +97,7 @@
ansible.builtin.fail:
msg: "Vaultwarden is running but did not answer /alive — see the diagnostics above."
# The database is the whole vault, and it arrives over the network that
# roles/postgres attached Postgres to. Check the name the environment file
# actually asks for, plus the shared `db` alias.
# `db` is the shared NetworkAlias, and the container name comes from the attach role, so both must resolve.
- name: Confirm Vaultwarden can resolve its database
ansible.builtin.command: >-
podman exec {{ container_name }} getent hosts {{ item }}
@@ -160,12 +120,7 @@
path: "{{ data_dir }}/db.sqlite3"
register: bitwarden_stale_sqlite
# SQLite in /data is the image's own default, and the Docker-era deployment left
# a stale db.sqlite3 (with -wal/-shm sidecars) behind long after DATABASE_URL had
# been pointed at Postgres. This deployment ignores the file entirely, which is
# exactly why it is dangerous: it looks like a restore source, and restoring it
# would silently lose months of vault data (docs/backup-plan.md §3.3). Warn
# rather than fail, because the file is inert while DATABASE_URL is set.
# The Docker-era db.sqlite3 looks like a restore source, but it is inert while DATABASE_URL is set, so warn rather than fail.
- name: Warn about the stale SQLite database
ansible.builtin.debug:
msg: >-
+4 -17
View File
@@ -1,27 +1,14 @@
---
# Caddy is its own playbook (caddy/playbook.yml) and a rootless Quadlet unit
# there. These handlers run as the connection user, so `systemctl --user` and
# `podman exec` work without any XDG_RUNTIME_DIR juggling.
# Notified by this role when the Caddyfile changes. A reload keeps existing
# connections; a restart would briefly drop every hosted site.
#
# Note the config path: /etc/caddy/Caddyfile is the mount inside the container,
# not {{ caddyfile_path }}, which is the host side of it.
# A reload keeps existing connections. A restart would briefly drop every hosted site.
# The config path is inside the container, not caddyfile_path on the host.
- name: Reload Caddy configuration
ansible.builtin.command: >-
podman exec {{ caddy_name }} caddy reload --config /etc/caddy/Caddyfile
changed_when: true
listen: "Reload Caddy"
# Notified by caddy/roles/register when this service's network unit, or the
# drop-in that attaches Caddy to it, is written. Quadlet has no live equivalent
# of `docker network connect`, so the container has to be recreated — a brief
# restart of every hosted site, not just this one. See caddy/readme.md,
# "Trade-off worth knowing".
#
# Defined after the reload above so that when both are notified the cheap
# config reload happens first.
# Quadlet has no live equivalent of network connect, so Caddy is recreated and every hosted site restarts.
# Defined after the reload so the cheap reload runs first.
- name: Restart Caddy
ansible.builtin.systemd_service:
name: "{{ caddy_name }}.service"
-11
View File
@@ -1,15 +1,4 @@
---
# Append this site's block to the shared Caddyfile.
#
# The Caddyfile is a bind mount into the Caddy container, so Caddy sees the
# change as soon as the file is written; the handler only asks Caddy to re-read
# it. That is a reload, not a restart, so the other hosted sites are untouched.
#
# blockinfile is idempotent on its own via the marker, so there is no need to
# slurp the file and compare first — which also matters because slurp *fails*
# when the Caddyfile does not exist yet, on a host where the Caddy playbook has
# not run.
- name: "Add the {{ host }} site block to the Caddyfile"
ansible.builtin.blockinfile:
path: "{{ caddyfile_path }}"
+1 -9
View File
@@ -1,13 +1,5 @@
---
# Postgres is its own playbook (postgres/playbook.yml) and a rootless Quadlet
# unit there. This handler runs as the connection user, so `systemctl --user`
# works without setting XDG_RUNTIME_DIR.
#
# Required by postgres/roles/attach, which notifies it whenever this app's
# drop-in changes: a new drop-in changes the generated unit, so Postgres must be
# recreated to join the network — Quadlet has no live equivalent of
# `podman network connect`. That is a few seconds of downtime for every attached
# app, not just this one. See postgres/readme.md, "The trade-off".
# A new drop-in changes the generated unit, so Postgres is recreated and every attached app restarts.
- name: Restart Postgres
ansible.builtin.systemd_service:
name: "{{ postgres_unit }}"
+1 -20
View File
@@ -1,26 +1,7 @@
---
# Attach this app's network to the shared Postgres unit.
#
# Postgres knows nothing about its clients, exactly like Caddy: a service joins
# by dropping 50-bitwarden.conf and the shared 10-network-aliases.conf next to
# postgres.container. The canonical way to do that is the shared include below —
# it writes the same bytes every service writes, asserts that bitwarden.network
# already exists, and notifies the "Restart Postgres" handler defined in this
# role.
#
# This runs in the roles phase, which is after the register include in
# playbook.yml's pre_tasks has created bitwarden.network.
- name: Attach Bitwarden to Postgres
ansible.builtin.include_tasks: "{{ playbook_dir }}/../postgres/roles/attach/tasks/main.yaml"
# On a first attach both drop-ins are new, so Postgres has to be recreated to
# join bitwarden_net — and that restart is a *handler*, which by default runs at
# the end of the play, after the bitwarden role has already started the app. The
# app would then come up before DATABASE_URL's host resolves, fail its own
# health check, and fail the play on a machine where nothing is actually wrong.
#
# Flushing applies the new drop-in before the app starts. On later runs the
# drop-ins are unchanged, nothing is pending, and this is a no-op.
# The restart that joins Postgres to bitwarden_net is a handler, so flush it before the app starts or the health check fails.
- name: Apply the Postgres network change before starting Vaultwarden
ansible.builtin.meta: flush_handlers