A brand-new config path (e.g. a role restructuring where its file lives) genuinely doesn't exist on disk under --check — the template task that would create it doesn't actually write in dry-run. slurp failed hard on that instead of skipping, caught deploying the nginx.conf directory-mount migration to configure-hyperion-router. Signed-off-by: Lain Iwakura <lain.iwakura@avallon.pw> |
||
|---|---|---|
| defaults | ||
| meta | ||
| tasks | ||
| README.md | ||
docker-config-verify
Post-deploy smoke test for any Docker service configured via ansible-templated
files. Catches the specific bug class where a single-file bind mount
(host/file:/container/file, as opposed to a directory bind) is pinned to a
stale inode after ansible.builtin.template (or any other tempfile+rename
atomic writer) replaces the host file — the container keeps serving old
content forever, no matter how many times you reload, until it's recreated.
nginx -t-style syntax checks don't catch this on their own: stale-but-valid
content passes them silently. See nikki-memory tech_notes.md: "Docker —
single-file bind mount + atomic rewrite = stale content".
Usage
Include it right after the tasks that template your config files and notify a reload/restart handler for them — it flushes those handlers first, so it's checking genuinely post-deploy state, not what was there before this run.
- name: Deploy nginx.conf
ansible.builtin.template:
src: nginx.conf.j2
dest: "{{ nginx_router_install_dir }}/nginx.conf"
notify: restart nginx-router # single-file mount -> needs restart, not reload
- name: Verify config reached the running container
ansible.builtin.include_role:
name: docker-config-verify
vars:
docker_config_verify_container: nginx-router
docker_config_verify_files:
- host: "{{ nginx_router_install_dir }}/nginx.conf"
container: /etc/nginx/nginx.conf
docker_config_verify_test_command: nginx -t
Fails the play with a clear message (naming the exact file and the fix) if
the container's view of any listed file doesn't match the host's rendered
copy. Files bind-mounted as a directory (e.g. conf.d/, letsencrypt/)
don't need this — a directory bind mount does a fresh path lookup on every
open(), so reload always sees current content there regardless of how
the file underneath was written.
Variables
| Variable | Required | Default | Description |
|---|---|---|---|
docker_config_verify_container |
yes | "" |
container name/ID to check |
docker_config_verify_files |
yes | [] |
list of {host, container} path pairs to byte-compare |
docker_config_verify_test_command |
no | "" |
command run inside the container after the comparison passes (e.g. nginx -t); skipped if empty |
docker_config_verify_fail_hint |
no | "" |
extra line appended to the failure message |
Why the file comparison runs before the test command
A stale single-file mount can still be syntactically valid — e.g. if the
only change was a comment or a value tweak, nginx -t on the old content
passes just fine, and you'd never know the change didn't apply. The byte
comparison is the real signal; the test command is a secondary check for
actual syntax errors in what you just deployed.