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]>
This commit is contained in:
Lucas Winther
2026-09-16 03:58:01 +02:00
co-authored by Claude Opus 5
parent 7b52e9af9e
commit b76c76e3b0
16 changed files with 399 additions and 0 deletions
@@ -0,0 +1,50 @@
# The public entry point. nginx-proxy watches the Docker socket and writes a
# vhost for every container that sets VIRTUAL_HOST; acme-companion requests and
# renews the certificate for every LETSENCRYPT_HOST.
#
# Two things about this stack drive decisions elsewhere in the repository:
#
# - proxy-net is external. Every stack that wants to be reachable joins it, so
# something has to create it before any of them start. That is stack_networks
# in the role, and it is why site.yml deploys this stack first.
# - conf/, vhost/ and html/ are bind mounts holding plain files. Editing one
# changes nothing until nginx reloads, because Compose sees an unchanged
# compose file and does not recreate the container. That is what
# compose_stack_synced and the post_tasks reload are for.
services:
reverse-proxy:
container_name: reverse-proxy
image: nginxproxy/nginx-proxy:1.6
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./proxy-data/conf:/etc/nginx/conf.d
- ./proxy-data/vhost:/etc/nginx/vhost.d
- ./proxy-data/html:/usr/share/nginx/html
- ./proxy-data/dhparam:/etc/nginx/dhparam
- ./proxy-data/certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- proxy-net
acme-companion:
container_name: reverse-proxy-acme
image: nginxproxy/acme-companion:2.4
restart: unless-stopped
volumes_from:
- reverse-proxy
volumes:
- ./proxy-data/certs:/etc/nginx/certs
- ./proxy-data/acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
# Let's Encrypt sends expiry warnings here.
DEFAULT_EMAIL: [email protected]
networks:
- proxy-net
networks:
proxy-net:
external: true
@@ -0,0 +1,3 @@
# Applies to every vhost. nginx defaults to 1M, which silently truncates ordinary
# file uploads.
client_max_body_size 100M;
@@ -0,0 +1,23 @@
# Per-vhost nginx configuration for app.example.com, included by nginx-proxy.
#
# A container that sets VIRTUAL_HOST gets its vhost generated automatically and
# needs nothing here. This file is for the cases generation cannot express:
# routes to a service that is not the vhost's main container, and access rules.
# Anything not otherwise routed goes to the public site.
location = / {
return 301 https://app.example.com/tools/;
}
# Internal-only: the metrics stack, reachable from the private network and
# nowhere else. Keep the deny rule directly beneath the allow rule — nginx takes
# the first match, so an allow added below a `deny all` does nothing.
location ^~ /internal/metrics/ {
allow 10.0.0.0/8;
deny all;
proxy_pass http://prometheus:9090/internal/metrics/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}