platform/platform-infra/stacks
Bart Van Geyt 92ee8ac05d fix(stacks/traefik): pin v3.7.10 so Docker provider works on Engine 29
Traefik v3.3's Docker provider fails to negotiate the API version against
Docker Engine 29 (API min 1.44) and falls back to 1.24, which the daemon
rejects ("client version 1.24 is too old"), leaving no container routes.
v3.7.x negotiates correctly through the socket-proxy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-09 03:52:45 +02:00
..
forgejo Phase 2: platform service stacks + CLAUDE.md 2026-07-07 16:58:18 +02:00
mariadb Phase 2: platform service stacks + CLAUDE.md 2026-07-07 16:58:18 +02:00
traefik fix(stacks/traefik): pin v3.7.10 so Docker provider works on Engine 29 2026-08-09 03:52:45 +02:00
bootstrap-networks.sh Phase 2: platform service stacks + CLAUDE.md 2026-07-07 16:58:18 +02:00
README.md Phase 2: platform service stacks + CLAUDE.md 2026-07-07 16:58:18 +02:00

Phase 2 — Platform services

Base Docker Compose projects that every customer site depends on. Each is its own compose project on the host, using data directories on the ZFS platform datasets created in Phase 1.

Stack Role Network(s) Data
traefik Edge router: TLS/ACME, dynamic routing, dashboard. Talks to Docker via a socket-proxy (least privilege). proxy, socketproxy tank/platform/traefik
mariadb Shared database. Per-site DB + user created later by the CLI. Not published to the host. platform tank/platform/mariadb
forgejo Git + container registry, git over HTTPS (via Traefik). proxy tank/platform/forgejo

Prerequisites

  • Phase 1 applied (ZFS datasets, Docker with the zfs driver, firewall).
  • DNS A/AAAA records for the platform hostnames (e.g. traefik.example.com, git.example.com) pointing at the host, so ACME can issue certificates.

Bring-up order

# 1. Shared networks (once)
./bootstrap-networks.sh

# 2. Traefik (edge first, so ACME/routing is ready)
cd traefik   && cp .env.example .env && $EDITOR .env && docker compose up -d && cd ..

# 3. Shared MariaDB
cd mariadb   && cp .env.example .env && $EDITOR .env && docker compose up -d && cd ..

# 4. Forgejo
cd forgejo   && cp .env.example .env && $EDITOR .env && docker compose up -d && cd ..

Secrets

For now each stack reads a git-ignored .env (copy from .env.example). Real secrets move to SOPS/age when the provisioning CLI lands (Phase 4). Generate strong values, e.g.:

openssl rand -base64 24                 # MariaDB root password
htpasswd -nbB admin 'your-password'     # Traefik dashboard basic-auth line

Verify

docker compose ls                                   # all three up
curl -I https://traefik.example.com                 # dashboard (401 = auth working)
docker exec -it mariadb mariadb -uroot -p -e 'SELECT VERSION();'
curl -I https://git.example.com                     # Forgejo reachable

Notes / hardening

  • Traefik ↔ Docker via socket-proxy: Traefik never mounts docker.sock directly. tecnativa/docker-socket-proxy exposes a read-only, minimal subset on an internal network. This limits blast radius if Traefik is compromised.
  • MariaDB is not published to the host; it is reachable only by containers on the internal platform network (sites join it to reach the DB).
  • Git over HTTPS only (SSH disabled) so no extra inbound port is needed — the Phase 1 firewall only opens 80/443 + admin SSH. Enable Forgejo SSH later only if you also open its port in the firewall.