Files
Lucas WintherandClaude Opus 5 b76c76e3b0 Add the example stack payloads
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]>
2026-09-16 03:58:01 +02:00

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.