Named-bridge-network production Compose (not host networking, unlike dev) for rn-platform-01; egress-proxy + agents for rn-lab-host-01. Caddy reverse proxy, single public origin, Grafana restricted to management_cidr. iptables-restore firewall rulesets for all three hosts (Proxmox host, rn-platform-01, rn-lab-host-01) codifying the Platform/Lab-Host boundary: no student-to-student traffic, no direct Proxmox exposure, no direct Postgres/Provisioner exposure from the Lab Host. Mutual TLS for the Provisioner<->Lab Host Docker connection: certificate generation/verification scripts, dockerd TLS config, firewall-restricted port 2376. Application support already existed since Phase 1; this is purely the operational half. Validated: docker compose config, caddy validate, firewall structural lint, and a real openssl s_server/s_client mTLS handshake proving the cert chain and mutual-auth rejection (legitimate client verified, rogue client correctly rejected with verify code 19).
2.4 KiB
Container image strategy
Images
| Image | Built from | Registry path |
|---|---|---|
rootnode/platform-api |
platform/api/Dockerfile |
{{ registry }}/rootnode/platform-api |
rootnode/gateway |
platform/gateway/Dockerfile |
{{ registry }}/rootnode/gateway |
rootnode/provisioner |
platform/provisioner/Dockerfile |
{{ registry }}/rootnode/provisioner |
rootnode/egress-proxy |
platform/egress-proxy/Dockerfile |
{{ registry }}/rootnode/egress-proxy |
rootnode/lab |
root Dockerfile (the student Lab image) |
{{ registry }}/rootnode/lab |
Tagging
Never :latest in staging or production. Every build is tagged with:
- the full git SHA (
sha-<40 hex>) — always, every build, the ground truth staging-<short-sha>on every push tomainthat passes CI (moving pointer, staging only)- a semantic tag (
vX.Y.Z) only on an explicit, human-triggered production release
infra/scripts/ci/tag-and-push.sh implements this — see §18 in
docs/PHASE4_PRODUCTION_INFRASTRUCTURE.md.
Deployment configs (infra/environments/*/*.env.template, Ansible group_vars) reference the
tag as a variable (image_tag), never a hardcoded value baked into a compose file — rolling
back is changing one variable and re-running the platform/lab-host Ansible role, not editing
a compose file by hand (see docs/RUNBOOK_ROLLBACK.md).
Registry
No paid registry required. Two supported options, chosen at deploy time via registry in
infra/ansible/group_vars/production.yml:
- Gitea's own built-in container registry (
git.synthossystems.com) — zero new infrastructure, already authenticated the same way the repo itself is. Preferred default. - A self-hosted registry (e.g. a
registry:2container on rn-platform-01 itself, or elsewhere in the homelab) — a documented fallback if the Gitea registry isn't enabled on this instance.
Either way: private registry, credentials pulled the same way any other production secret is
(docs/PRODUCTION_DEPLOYMENT.md §10), never a public Docker Hub push of anything beyond what's
already public (this project doesn't publish anything to Docker Hub in Phase 4).
Rollback
Every image is content-addressable by SHA tag, so "roll back" is always available as long as
the previous tag hasn't been garbage-collected from the registry — see
docs/RUNBOOK_ROLLBACK.md for the exact procedure (re-point image_tag, re-run the Ansible
role, verify).