The files copied to /srv/stacks/<stack>/ verbatim: compose files, vhosts, Prometheus configuration, the webapp image source. Payloads are data, never templated and never linted as Ansible content. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2.9 KiB
server/ — the payloads
Everything under this directory is data. These files are copied to the hosts verbatim: no templating, no variable substitution by Ansible, no generation.
server/<host>/<stack>/ a stack that runs on exactly one host
server/shared/<stack>/ a payload used by more than one host or instance
server/shared/components/ single files that belong to another stack's directory
The directory names under server/ must match the inventory host names, because
the role resolves a payload as server/{{ inventory_hostname }}/{{ stack_name }}
unless a playbook overrides stack_src.
server/ is excluded from ansible-lint and yamllint. A compose file is not
Ansible content, and most of these come from upstream projects that format them
their own way — linting them produces noise and pressure to reformat files you
want to be able to diff against upstream.
The examples
All six are fictional. example.com is reserved by RFC 2606 and can never
resolve to a real service. Replace them with your own; the framework around them
is the reusable part.
| Payload | Used by | Notes |
|---|---|---|
edge/reverse-proxy/ |
reverse-proxy.yml |
nginx-proxy + acme-companion. Owns proxy-net. Its proxy-data/ subdirectories are bind mounts holding plain config files. |
app/static-site/ |
static-site.yml |
One container, one network, no state. The simplest thing that works. |
app/webapp/ |
webapp.yml, webapp-staging.yml |
Postgres plus an app built from app/Dockerfile. A second compose file holds maintenance-only services. Every credential comes from .env. |
shared/metrics/ |
metrics.yml |
Prometheus and Grafana, identical on both hosts, so it lives here rather than being duplicated per host. |
shared/components/banner.html |
banner.yml |
A single file that belongs to the reverse proxy's directory on every host. |
Conventions worth copying
Take secrets from .env, and require them. Every credential in these
payloads is written as ${VAR:?message}. Compose then refuses to start and names
the missing variable, instead of expanding it to an empty string and starting the
service with no password. Ship a .env.example listing the names with no values;
the role never syncs .env itself.
Pin image tags. nginx:1.27-alpine, not nginx:latest. A latest tag means
a plain deploy will not pick up a new build (Compose keeps the image it has) and
an update run picks up whatever was published this morning. Both are surprises.
Declare shared networks external. A network two stacks need is created by the
playbook through stack_networks, not by whichever compose file happened to run
first. That is what lets every playbook stand on its own.
Do not put host paths in the compose file. Use ${PWD} if a sibling
container needs one — the role exports it as the stack's directory on the host,
which is the only value that is correct from inside a deploy.