Traefik Behind Traefik (tcp router) : A Step-by-Step Guide
Introduction
Setting up Traefik behind Traefik allows for a flexible, scalable, and secure reverse proxy architecture. In this guide, we'll configure a primary Traefik instance that passes TCP routes (without SSL termination) to secondary Traefik instances. These secondary instances reside on separate VMs and can run Docker or Kubernetes environments, handling SSL termination at their level.
Traefik TCP Routers
Traefik TCP routing is similar to AWS Gateway Load Balancer (GWLB).
However, GWLB itself does not handle SNI directlyβit works with third-party appliances (e.g., NGINX, HAProxy, or Traefik) that can inspect SNI and forward traffic accordingly.
Architecture Overview
graph TD
Internet["π Internet"]
PrimaryTraefik["π Traefik (Primary)
*.domain1.com
*.domain2.com"]
Secondary1["π Traefik Secondary VM1
π *.domain1.com"
π TLS / Certificate]
Secondary2["π Traefik Secondary VM2
π *.domain2.com"
π TLS / Certificate]
subgraph "Services (VM2)"
Service2c["π Nginx"]
Service2d["ποΈ MongoDB"]
end
subgraph "Services (VM1)"
Service1c["π Nginx"]
Service1d["ποΈ MongoDB"]
end
Internet -->|TCP Traffic| PrimaryTraefik
PrimaryTraefik -->|TCP Route 1 | Secondary1
PrimaryTraefik -->|TCP Route 2 | Secondary2
Secondary1 --> Service1c
Secondary1 --> Service1d
Secondary2 --> Service2c
Secondary2 --> Service2d
Why Use This Setup?
- Scalability: Distribute traffic across multiple secondary instances.
- Security: Offload SSL termination to the secondary instances.
- Flexibility: Use different environments (Docker, Kubernetes) for different services.
Prerequisites
- A primary Traefik instance running on a VM.
- Secondary Traefik instances running on separate VMs (Docker or Kubernetes-based).
- DNS records pointing to the primary Traefik instance.
- Basic knowledge of Traefik configuration and networking.
Step 1: Configure the Primary Traefik Instance
Install Traefik
https://github.com/jdedev/tophomelab/blob/main/docker/traefik3/docker-compose.yml
services:
traefik3:
image: traefik:v3.0
container_name: traefik
restart: always
command:
- "--configFile=/etc/traefik/traefik.yml" # Load static config from file
environment:
- TZ=Australia/Melbourne
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard (if insecure mode is enabled)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik.yml:/etc/traefik/traefik.yml:ro" # Mount static config
- "./dynamic.yml:/etc/traefik/dynamic.yml:ro" # Mount dynamic config
- "./logs:/var/log" # Mount logs to local directory
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik3.homelab.lan`)"
- "traefik.http.routers.traefik.entrypoints=web"
- "traefik.http.routers.traefik.service=api@internal"
Update traefik.yml
https://github.com/jdedev/tophomelab/blob/main/docker/traefik3/traefik.yml
Add reference to a dynamic configuration file
# Enable Docker & File Provider
providers:
docker:
exposedByDefault: false
file:
filename: "/etc/traefik/dynamic.yml" # Loads dynamic configuration
watch: true # Auto-reload on changes
Update dynamic.yml
Route different domains to secondary Traefik instances / VMs.
Here we are using regular expressions to route traffic based on the servername.
https://doc.traefik.io/traefik/routing/routers/#hostsni-and-hostsniregexp
- domain1.com → 10.0.5.111:443
- domain2.com → 10.0.6.101:443
https://github.com/jdedev/tophomelab/blob/main/docker/traefik3/dynamic.yml
tcp:
routers:
domain1-router:
entryPoints:
- websecure # This should match the entrypoint for 443
rule: "HostSNIRegexp(`^.+\\.domain1\\.com`)"
service: domain1-service
tls:
passthrough: true
domain2-router:
entryPoints:
- websecure # This should match the entrypoint for 443
rule: "HostSNIRegexp(`^.+\\.domain2\\.com`)"
service: domain2-service
tls:
passthrough: true
services:
domain1-service:
loadBalancer:
servers:
- address: "10.0.5.111:443" # Forward traffic to the VM
domain2-service:
loadBalancer:
servers:
- address: "10.0.6.101:443" # Forward traffic to the VM
Step 2: Configure the Secondary Traefik Instances
Update traefik.yml on Secondary Traefik
Enable Let's Encrypt
certificatesResolvers:
letsencrypt:
acme:
email: "[email protected]"
storage: "/etc/traefik/acme.json"
httpChallenge:
entryPoint: "web"
Step 3: Testing the Setup
- Run Traefik on both primary and secondary instances.
- Verify traffic is routed through the primary Traefik and reaches the secondary Traefik.
- Check SSL termination at the secondary Traefik instance using
Response
* Trying 12.34.56.78:443...
* Connected to domain1.com (12.34.56.78) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
Source Code
You can find the source code for this setup in the following GitHub repository:
https://github.com/jdedev/tophomelab/tree/main/docker/traefik3
Conclusion
By setting up Traefik behind Traefik, you achieve a modular, scalable, and secure proxy setup. This allows different environments to handle SSL termination while maintaining centralized routing control.