Files
config-public/server/README.md
T
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

54 lines
2.9 KiB
Markdown

# 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.