The goal is intentionally simple:
- Traefik handles public HTTP/HTTPS ingress, ACME DNS challenges, HTTP/3, Docker labels, and dynamic file config.
- HAProxy handles primary/backup failover for HTTP and raw TCP services reachable across a mesh VPN/private network.
- Tailscale is included as an optional sidecar, but any mesh VPN/VPN/private routing layer can provide backend reachability.
- Cloudflare DNS is the default ACME example, but Traefik supports many DNS providers.
It contains placeholders only: no real domains, tokens, Tailnet state, private IP inventory, ACME certs, or environment files.
compose.yml Docker Compose stack
traefik.yml Traefik static configuration
host/ Traefik dynamic file provider examples
middleware.yml Shared middlewares
apps.yml Example public app routes through HAProxy
streams.yml Example TCP passthrough route
auth.yml Optional Authentik forwardAuth callback route
haproxy/haproxy.cfg HAProxy frontends/backends for failover
watcher/ Optional HAProxy stats watcher + Dockhand failover hooks
docs/ Architecture and customization notes
-
Clone the repo
-
Copy the environment template and make your edits:
cp .env.example .envEdit the example files for your domain, DNS provider, VPN/mesh addresses, and backend services:
.envcompose.ymltraefik.ymlhost/*.ymlhaproxy/haproxy.cfgwatcher/config.ymlif using the optional watcher
- Deploy the base ingress:
docker compose up -d traefik haproxyOptional profiles:
# Add the Tailscale sidecar for Traefik
docker compose --profile tailscale up -d
# Add the HAProxy failover watcher
docker compose --profile watcher up -d watcher- Replace every
example.comhostname. - Replace every
100.64.0.xbackend address with addresses reachable from the ingress host/container. - Replace ACME email in
traefik.yml. - If not using Cloudflare, change:
CF_DNS_API_TOKENin.env.example/.env- the environment variable in
compose.yml certificatesResolvers.*.acme.dnsChallenge.providerintraefik.yml
- Keep
.env,certs/, andtailscale/state/out of git. - Expose HAProxy stats only through Traefik or localhost; do not publish it openly without authentication.
This pattern keeps ingress portable, modular, and easy to reason about. Most behavior lives in YAML and plain config files, so the stack can be reviewed, copied, versioned, and transferred between environments without depending on a heavy control plane.
It is designed around interchangeable pieces:
- DNS provider: Cloudflare is the default example, but Traefik supports many ACME DNS-01 providers.
- Private connectivity: Tailscale is the included sidecar example, but any mesh VPN, traditional VPN, or private routing layer can work.
- Routing: Traefik dynamic files make HTTP, middleware, auth, and TCP routing easy to split into focused config files.
- Failover: HAProxy provides straightforward health-check based primary/backup routing for HTTP and TCP services.
- Automation: the watcher is optional and can be adapted to whatever orchestrator starts or stops backup stacks.
The result is a lightweight ingress system that can support many different configurations while staying transparent: edit YAML, edit HAProxy config, reload/redeploy, and move the same pattern wherever the network paths exist.