Skip to main content

Architecture

Four layers on one host: edge, observability, secrets, and the infrastructure automation underneath them.
  • Edge — Traefik reverse proxy with automatic HTTPS, Portainer, and the DNS-01 challenge webhook
  • Observability — the LGTM stack (Loki, Grafana, Tempo, Prometheus) with collectors
  • Secrets — OpenBao, with SOPS/age as bootstrap and disaster-recovery backup
  • Infrastructure — Docker Compose orchestration, Tailscale VPN for admin access, Ansible playbooks for host provisioning

Network topology

Network isolation

docker-compose.infra.yml is the sole owner of all three networks. The observability and vault stacks declare them external: true, which is why the edge stack must deploy first on a fresh host. Admin surfaces are additionally restricted to the Tailscale CGNAT range (100.64.0.0/10) by a Traefik IP-allowlist middleware, and SSH is restricted to the same range at the firewall.

Service responsibilities

Security posture

  • Public internet access is limited to ports 80 and 443, handled by Traefik. Every administrative surface is reachable only through Tailscale.
  • SSH uses key-based authentication only; password auth and root login are disabled, and fail2ban is enabled.
  • The Traefik dashboard password hash is generated at deploy time from encrypted secrets into a gitignored .htpasswd.
  • ACME state is persisted in mounted Traefik storage so renewals survive restarts.
  • CI blocks destructive Docker commands (down -v, volume rm, system prune, --remove-orphans) from entering scripts or workflows.
  • No service in the current stack sends email. Keycloak was the only sender and went with the application.
The verification checklist for the posture above — make health, public-versus-Tailscale SSH, make dns-verify, ACME issuance in the Traefik logs, ten containers in docker ps — lives in the security architecture doc linked below.

Source documents

These are maintained beside the code and are the authority. This page summarises them.