-
Notifications
You must be signed in to change notification settings - Fork 55
Docker Compose Deployment behind Traefik Reverse Proxy
Snawoot edited this page Jul 30, 2025
·
14 revisions
This example exposes dumbproxy via Traefik reverse proxy, routing connections by domain in SNI of TLS handshake. TLS connection is handled on Traefik side, dumbproxy gets plaintext HTTP connection. Original client address is preserved and passed to dumbproxy (-proxyproto option).
docker-compose.yaml:
version: "3.8"
networks:
internal:
external: false
traefik_proxy:
external: true
secrets:
users:
file: ./configs/users.conf
services:
traefik:
image: traefik:v3.5.0
deploy:
restart_policy:
condition: any
environment:
TZ: Etc/UTC
logging:
driver: json-file
options:
max-file: "1"
max-size: 90m
networks:
traefik_proxy:
ports:
- mode: host
target: 80
published: 80
protocol: tcp
- mode: host
target: 443
published: 443
protocol: tcp
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
- type: bind
source: ./configs/traefik.yaml
target: /etc/traefik/traefik.yaml
read_only: true
- type: bind
source: ./configs/dynamic.yaml
target: /etc/traefik/dynamic.yaml
read_only: true
- type: bind
source: ./configs/acme.json
target: /etc/traefik/acme.json
read_only: false
dumbproxy-http:
image: ghcr.io/senseunit/dumbproxy:1.27.0
command:
- -bind-address=:8080
- -auth=basicfile://?path=/run/secrets/users.conf&hidden_domain=opennet.ru&reload=10s
- -proxyproto
- -verbosity=20
deploy:
labels:
traefik.enable: "true"
traefik.tcp.routers.dumbproxy.rule: HostSNI(`<your-domain.com>`)
traefik.tcp.services.dumbproxy.loadBalancer.server.port: "8080"
traefik.tcp.routers.dumbproxy.tls.passthrough: "false"
dns:
- 208.67.222.222
- 208.67.220.220
logging:
driver: json-file
options:
max-file: "1"
max-size: 100m
networks:
internal:
traefik_proxy:
secrets:
- source: users
target: /run/secrets/users.confconfigs/traefik.yaml:
global:
checkNewVersion: false
sendAnonymousUsage: false
log:
level: INFO
noColor: false
filePath: /dev/stdout
accessLog:
filePath: /dev/stdout
providers:
swarm:
exposedByDefault: false
endpoint: unix:///var/run/docker.sock
network: traefik_proxy
watch: true
file:
filename: /etc/traefik/dynamic.yaml
watch: true
api:
dashboard: false
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: :443
forwardedHeaders:
insecure: true
http:
tls:
certResolver: letsencrypt
middlewares:
- default@file
certificatesResolvers:
letsencrypt:
acme:
email: <your-email>
storage: /etc/traefik/acme.json
tlsChallenge: {}configs/dynamic.yaml:
tls:
options:
default:
minVersion: VersionTLS12
maxVersion: VersionTLS13
sniStrict: true
curvePreferences:
- CurveP521
- CurveP384
alpnProtocols:
- http/1.1
- h2
- acme-tls/1
http:
middlewares:
default:
headers:
browserXssFilter: true
referrerPolicy: strict-origin-when-cross-origin
stsPreload: true
sslRedirect: true
sslTemporaryRedirect: true
sslForceHost: true
stsSeconds: "31536000"
forceSTSHeader: true
frameDeny: true
contentSecurityPolicy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'
contentSecurityPolicyReportOnly: default-src 'self'; script-src 'self' 'nonce-{random}'
contentTypeNosniff: true
customResponseHeaders:
X-Forwarded-Server: traefik
Permissions-Policy: camera=(), microphone=(), geolocation=()
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin