Files
best-practices/ansible.md
Paul O'Reilly 8aa400a5d4 distill: 49 best practices from 5 projects (2026-03-27..2026-04-05)
Add 37 new entries and update 7 existing entries across 13 topic files.
Major contributions from agent-runtimes (K8s secrets, CI, Docker gotchas),
cluster-bootstrap (ArgoCD SSA, etcd tuning, DB migrations, Compose networking),
and cluster-apps/octopus-deploy (Helm vs raw manifests, ArgoCD source types).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:10:07 +12:00

2.7 KiB

Ansible

Inventory and Execution

  • Always pass -i inventory.yml explicitly or run from the directory containing ansible.cfg
  • Playbooks that can't find inventory skip silently with no error — a common source of "it ran but nothing happened" confusion
  • Variables that need customisation go in inventory.yml files, not scattered across role defaults

Role Structure

  • Roles follow standard structure: tasks/main.yml, templates/*.j2, handlers/main.yml
  • Jinja2 templates have .j2 extension and include a "managed by Ansible" header comment

Template Safety

  • Never use placeholder values with -e for vars that template config files. Using -e "var=dummy" will overwrite live configs with garbage. Either read real values, use --skip-tags to skip templating tasks, or restructure roles so sensitive templates are in a separate tag.

Credential Safety

  • Pass secrets via @file not -e on the command line — -e "key=value" exposes secrets in ps output
  • Use temp files with trap rm cleanup: -e "@${tmpfile}"

Module Gotchas

  • docker_compose_v2state: restarted is supported since community.docker 3.7.0. The old workaround (recreate: always) still works but is no longer necessary
  • ansible.builtin.unarchive with remote_src and extra_opts: --strip-components is unreliable — use get_url + command: tar separately
  • get_url won't re-download when the URL changes but the destination filename stays the same — use a version marker file to detect changes

Service Restarts

  • Some services (dnsmasq, etc.) need container restarts for config changes to take effect
  • Ansible handlers handle this, but always verify the change took effect (e.g., dig @<ip> <record> +short)

Docker Compose

  • network_mode: host ignores ports: mappings — remove ports: to avoid warnings

SOPS Vars Plugin Requires Running From ansible.cfg Directory

The community.sops.sops vars plugin is configured in ansible.cfg. Running ansible-playbook from a different directory (even with -i /path/to/inventory.yml) fails because the SOPS plugin isn't loaded, causing undefined variable errors for decrypted secrets. Always cd to the directory containing ansible.cfg before running playbooks that rely on SOPS-encrypted group_vars.

Ansible file Task on Existing Directories Has Side Effects

An ansible.builtin.file task that ensures a directory exists (state: directory, owner/group/mode) will also change any pre-existing directory that doesn't exactly match, even if it belongs to a different service. In roles that manage multiple services, this can cause cross-service side effects. Scope directory tasks tightly with conditionals or use service-specific variable names.