2.6 KiB
ADR-0001: Traefik TCP TLS passthrough load balancer
Date: 2026-06-30
Status: Accepted
Context
The load balancer must run as a Docker container and forward TCP TLS traffic to backend services reachable by local IP address and port. Backend services already expose their own SSL certificates, so the load balancer must not terminate TLS or store backend private keys. Runtime configuration must be kept in a host-based volume.
Decision
Use Traefik Proxy in Docker with the file provider and TCP TLS passthrough routing. Static and dynamic configuration live under the host ./config directory and are mounted read-only at /etc/traefik.
The public host port 443/tcp is mapped to container port 8443/tcp so the container can run with all Linux capabilities dropped. Traefik routes incoming TLS connections with HostSNI(...) to the configured TCP load-balancer services.
The public host port 80/tcp is mapped to container port 8080/tcp for HTTP-01 certificate challenge traffic. HTTP challenge routes use Host(...) and PathPrefix('/.well-known/acme-challenge/') because SNI exists only in TLS handshakes.
Consequences
TLS certificates, private keys, and certificate renewal remain the responsibility of each backend. Traefik can route TLS traffic based on the TLS SNI value but cannot inspect HTTP headers or paths because TLS is passed through unchanged.
Backend health is limited to TCP reachability unless protocol-specific health checks are added later. Configuration changes in config/dynamic/ are watched by Traefik and can be applied without recreating the container.
Alternatives considered
TLS termination at Traefik was rejected because the requested backend-owned certificate model would require moving certificates or keys to Traefik.
The Docker provider was not selected because the backends are addressed directly by IP and port rather than discovered from Docker labels.
Security impact
The container does not mount the Docker socket, does not run privileged, drops Linux capabilities, uses no-new-privileges, and mounts configuration read-only. No private keys or credentials are required in this repository. Public 80/tcp exposure is limited by HTTP routers to ACME challenge paths for configured hostnames.
Operational impact
Operators update backend IP addresses and ports in config/dynamic/tcp-services.yml. The Compose deployment publishes 80/tcp and 443/tcp; the ping entrypoint is internal to the container and used by the healthcheck.
Rollback
Revert the commit that introduced the Traefik configuration and restore any previous load-balancer service. If deployed, stop the Compose stack with docker compose down.