When you run multiple Docker applications on a single server, you need a reverse proxy to route incoming traffic to the right container. Traefik is purpose-built for this: it integrates natively with Docker, discovers services automatically through container labels, provisions Let's Encrypt SSL certificates without manual intervention, and provides a real-time monitoring dashboard — all from a single Docker container.
This guide walks through the complete Traefik setup on an Ubuntu VPS: installing Traefik as a Docker container, configuring automatic HTTPS with Let's Encrypt, deploying multiple applications with Docker labels for service discovery, setting up the monitoring dashboard, implementing rate limiting and security middleware, and hardening the entire stack for production use.
Prerequisites
Before starting, you need:
- An Ubuntu 24.04 VPS. For running multiple Docker applications behind Traefik, deploy a Cloud VPS with 2 vCPU / 4 GB RAM — this gives you enough headroom for Traefik plus several application containers.
- Root or sudo access. If you haven't configured your server yet, follow our Ubuntu VPS setup guide and security hardening guide first.
- Docker and Docker Compose installed. Follow our Docker installation guide if you haven't set these up.
- One or more domain names with A records pointing to your server's IP address.
- Ports 80 and 443 open in your firewall.
MassiveGRID Ubuntu VPS — Ubuntu 24.04 LTS pre-installed, Proxmox HA cluster with automatic failover, Ceph 3x replicated NVMe storage, independent CPU/RAM/storage scaling, 12 Tbps DDoS protection, 4 global datacenter locations, 100% uptime SLA, and 24/7 human support rated 9.5/10. Deploy a Cloud VPS from $1.99/mo.
Why Traefik Over Nginx for Docker
Both Nginx and Traefik work as reverse proxies, but they differ fundamentally in how they discover and route to backend services.
With Nginx as a reverse proxy (covered in our Nginx reverse proxy guide), you manually write configuration files for each application, obtain SSL certificates with Certbot, and reload Nginx whenever you add or remove a service. This works well for static deployments but becomes tedious when you frequently deploy, scale, or replace containers.
With Traefik, you define routing rules as Docker labels on each container. When a container starts, Traefik detects it automatically and begins routing traffic. When a container stops, Traefik removes the route. SSL certificates are provisioned and renewed without any manual steps. No configuration file edits, no reloads.
| Aspect | Nginx + Certbot | Traefik |
|---|---|---|
| Service discovery | Manual config files | Automatic via Docker labels |
| SSL certificates | Certbot (separate tool) | Built-in Let's Encrypt |
| Adding a new app | Write config, get cert, reload | Add labels, start container |
| Removing an app | Delete config, reload | Stop container (automatic) |
| Dashboard | Requires separate setup | Built-in web UI |
| Middleware | Config directives | Composable middleware chain |
| Best for | Static deployments, high performance | Dynamic Docker environments |
Use Traefik when you run multiple Docker containers that change frequently. Use Nginx when you have a fixed set of services and need maximum raw performance or fine-grained configuration control.
Project Directory Structure
Create a clean directory structure for Traefik and your applications:
sudo mkdir -p /opt/traefik
sudo mkdir -p /opt/apps
All Traefik configuration will live in /opt/traefik, and individual application compose files in /opt/apps/ subdirectories.
Creating the Docker Network
Traefik and your application containers need to share a Docker network. Create an external network that all compose files will reference:
docker network create web
This network allows Traefik to communicate with any container attached to it, regardless of which Docker Compose project the container belongs to.
Traefik Configuration
Traefik uses two types of configuration: static configuration (loaded once at startup) and dynamic configuration (discovered from Docker labels or file providers at runtime).
Create the static configuration file:
sudo nano /opt/traefik/traefik.yml
# Traefik static configuration
# API and dashboard
api:
dashboard: true
insecure: false
# Entry points (ports Traefik listens on)
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
# Certificate resolvers
certificatesResolvers:
letsencrypt:
acme:
email: admin@yourdomain.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
# Docker provider
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: web
# Logging
log:
level: INFO
accessLog:
filePath: "/var/log/traefik/access.log"
bufferingSize: 100
Key configuration options explained:
entryPoints.web— listens on port 80 and redirects all traffic to HTTPSentryPoints.websecure— listens on port 443 with TLS enabledcertificatesResolvers.letsencrypt— automatic Let's Encrypt certificate provisioning using the HTTP-01 challengeproviders.docker— tells Traefik to watch the Docker socket for container eventsexposedByDefault: false— containers are NOT exposed unless they explicitly opt in with the labeltraefik.enable=true
Create the certificate storage file with the correct permissions:
sudo mkdir -p /opt/traefik/letsencrypt
sudo touch /opt/traefik/letsencrypt/acme.json
sudo chmod 600 /opt/traefik/letsencrypt/acme.json
Create the log directory:
sudo mkdir -p /var/log/traefik
Deploying Traefik with Docker Compose
Create the Traefik Docker Compose file:
sudo nano /opt/traefik/docker-compose.yml
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
- /var/log/traefik:/var/log/traefik
networks:
- web
labels:
# Enable Traefik for itself (for the dashboard)
- "traefik.enable=true"
# Dashboard router
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
# Dashboard authentication (basic auth)
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$your-bcrypt-hash-here"
networks:
web:
external: true
Generating the Dashboard Password
The dashboard should be protected with authentication. Generate a bcrypt-hashed password using htpasswd:
sudo apt install -y apache2-utils
htpasswd -nbB admin your-secure-password
This outputs something like:
admin:$2y$05$Gf3aX...long-hash...
In the Docker Compose file, escape every $ sign by doubling it ($$). Replace the basicauth.users label value with your generated hash (with escaped dollar signs).
Starting Traefik
cd /opt/traefik
docker compose up -d
Verify Traefik is running:
docker compose logs -f traefik
You should see Traefik starting up, connecting to Docker, and listening on ports 80 and 443. If everything is working, the dashboard will be accessible at https://traefik.yourdomain.com (after DNS propagation and certificate issuance).
Deploying Multiple Applications
Now let's deploy several applications behind Traefik. Each application gets its own Docker Compose file with Traefik labels defining the routing rules.
Application 1: WordPress
sudo mkdir -p /opt/apps/wordpress
sudo nano /opt/apps/wordpress/docker-compose.yml
services:
wordpress:
image: wordpress:6.7-php8.3-apache
container_name: wordpress
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: wordpress-db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: your-db-password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
networks:
- web
- wordpress-internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.rule=Host(`blog.yourdomain.com`)"
- "traefik.http.routers.wordpress.entrypoints=websecure"
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
wordpress-db:
image: mariadb:11
container_name: wordpress-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: your-root-password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: your-db-password
volumes:
- wordpress_db_data:/var/lib/mysql
networks:
- wordpress-internal
volumes:
wordpress_data:
wordpress_db_data:
networks:
web:
external: true
wordpress-internal:
internal: true
Start the WordPress stack:
cd /opt/apps/wordpress
docker compose up -d
Traefik automatically detects the WordPress container, provisions an SSL certificate for blog.yourdomain.com, and routes HTTPS traffic to it. The MariaDB container is on an internal network and is not exposed to Traefik.
Application 2: REST API (Node.js)
sudo mkdir -p /opt/apps/api
sudo nano /opt/apps/api/docker-compose.yml
services:
api:
image: node:22-alpine
container_name: api
restart: unless-stopped
working_dir: /app
command: node server.js
volumes:
- ./app:/app
environment:
NODE_ENV: production
PORT: 3000
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.yourdomain.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=3000"
# Rate limiting for API
- "traefik.http.routers.api.middlewares=api-ratelimit"
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.api-ratelimit.ratelimit.period=1m"
networks:
web:
external: true
cd /opt/apps/api
docker compose up -d
Application 3: Static Site (Nginx)
sudo mkdir -p /opt/apps/static-site
sudo mkdir -p /opt/apps/static-site/html
sudo nano /opt/apps/static-site/docker-compose.yml
services:
static-site:
image: nginx:1.27-alpine
container_name: static-site
restart: unless-stopped
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- web
labels:
- "traefik.enable=true"
- "traefik.http.routers.static-site.rule=Host(`www.yourdomain.com`)"
- "traefik.http.routers.static-site.entrypoints=websecure"
- "traefik.http.routers.static-site.tls.certresolver=letsencrypt"
- "traefik.http.services.static-site.loadbalancer.server.port=80"
# Cache headers for static content
- "traefik.http.routers.static-site.middlewares=static-headers"
- "traefik.http.middlewares.static-headers.headers.customresponseheaders.Cache-Control=public, max-age=86400"
networks:
web:
external: true
cd /opt/apps/static-site
docker compose up -d
Application 4: Adminer (Database Management)
sudo mkdir -p /opt/apps/adminer
sudo nano /opt/apps/adminer/docker-compose.yml
services:
adminer:
image: adminer:latest
container_name: adminer
restart: unless-stopped
networks:
- web
- wordpress-internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.adminer.rule=Host(`db.yourdomain.com`)"
- "traefik.http.routers.adminer.entrypoints=websecure"
- "traefik.http.routers.adminer.tls.certresolver=letsencrypt"
- "traefik.http.services.adminer.loadbalancer.server.port=8080"
# IP whitelist — only allow from your IP
- "traefik.http.routers.adminer.middlewares=adminer-ipwhitelist"
- "traefik.http.middlewares.adminer-ipwhitelist.ipallowlist.sourcerange=YOUR.PUBLIC.IP.ADDRESS/32"
networks:
web:
external: true
wordpress-internal:
external: true
name: wordpress_wordpress-internal
cd /opt/apps/adminer
docker compose up -d
Understanding Docker Labels for Traefik
Every application container uses Docker labels to tell Traefik how to route traffic. Here's a breakdown of the essential labels:
| Label | Purpose |
|---|---|
traefik.enable=true |
Opt this container in for Traefik routing |
traefik.http.routers.NAME.rule |
Match condition (Host, Path, Headers, etc.) |
traefik.http.routers.NAME.entrypoints |
Which entry point to listen on (web, websecure) |
traefik.http.routers.NAME.tls.certresolver |
Which certificate resolver to use |
traefik.http.services.NAME.loadbalancer.server.port |
Container port to forward traffic to |
traefik.http.routers.NAME.middlewares |
Comma-separated list of middleware to apply |
Advanced Routing Rules
Traefik's routing rules go beyond simple host matching:
# Host-based routing
- "traefik.http.routers.app.rule=Host(`app.yourdomain.com`)"
# Path-based routing
- "traefik.http.routers.app.rule=Host(`yourdomain.com`) && PathPrefix(`/api`)"
# Multiple hosts
- "traefik.http.routers.app.rule=Host(`yourdomain.com`) || Host(`www.yourdomain.com`)"
# Path stripping (remove /api prefix before forwarding)
- "traefik.http.routers.app.rule=Host(`yourdomain.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.app.middlewares=strip-api"
- "traefik.http.middlewares.strip-api.stripprefix.prefixes=/api"
# Header-based routing
- "traefik.http.routers.app.rule=Host(`api.yourdomain.com`) && Headers(`X-Api-Version`, `v2`)"
Automatic SSL with Let's Encrypt
Traefik's built-in ACME client handles the entire certificate lifecycle automatically. When a new router with tls.certresolver=letsencrypt is detected, Traefik:
- Checks if a certificate for the domain already exists in
acme.json - If not, initiates the HTTP-01 challenge on port 80
- Stores the obtained certificate in
acme.json - Automatically renews certificates before they expire
All certificates are stored in a single file (acme.json). This file must have mode 600:
ls -la /opt/traefik/letsencrypt/acme.json
# -rw------- 1 root root ... acme.json
Using DNS Challenge for Wildcard Certificates
For wildcard certificates, switch from HTTP-01 to DNS-01 challenge. Update traefik.yml:
certificatesResolvers:
letsencrypt:
acme:
email: admin@yourdomain.com
storage: /letsencrypt/acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Add Cloudflare API credentials to the Traefik container's environment in docker-compose.yml:
environment:
- CF_DNS_API_TOKEN=your-cloudflare-api-token
Then use a wildcard certificate on any router:
labels:
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.routers.app.tls.domains[0].main=yourdomain.com"
- "traefik.http.routers.app.tls.domains[0].sans=*.yourdomain.com"
For more details on Let's Encrypt certificates, see our Let's Encrypt SSL guide.
Dashboard and Monitoring
Traefik's built-in dashboard provides a real-time view of all routers, services, and middleware. With the configuration above, the dashboard is accessible at https://traefik.yourdomain.com with basic authentication.
The dashboard shows:
- Routers — all active routing rules and their status
- Services — backend services and their health
- Middlewares — applied middleware chains
- Entrypoints — listening ports and protocols
Prometheus Metrics
Traefik can expose Prometheus-compatible metrics. Add to traefik.yml:
metrics:
prometheus:
entryPoint: websecure
addEntryPointsLabels: true
addServicesLabels: true
addRoutersLabels: true
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
The metrics endpoint is available at /metrics on the Traefik container. Connect this to Prometheus and Grafana for historical monitoring. For a complete monitoring setup, see our Ubuntu VPS monitoring guide.
Access Logs
Access logs are configured in traefik.yml and written to /var/log/traefik/access.log on the host. Rotate these logs with a cron job or logrotate configuration (see our cron jobs and task scheduling guide):
sudo nano /etc/logrotate.d/traefik
/var/log/traefik/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
postrotate
docker kill --signal=USR1 traefik 2>/dev/null || true
endscript
}
Middleware: Rate Limiting, Headers, and More
Traefik middleware processes requests between the entry point and the backend service. Middleware is composable — you can chain multiple middleware together.
Rate Limiting
labels:
- "traefik.http.routers.api.middlewares=api-ratelimit"
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.api-ratelimit.ratelimit.period=1m"
This allows 100 requests per minute per source IP, with a burst capacity of 50 additional requests.
Security Headers
labels:
- "traefik.http.routers.app.middlewares=security-headers"
- "traefik.http.middlewares.security-headers.headers.stsSeconds=63072000"
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
- "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.security-headers.headers.frameDeny=true"
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.security-headers.headers.referrerPolicy=strict-origin-when-cross-origin"
- "traefik.http.middlewares.security-headers.headers.permissionsPolicy=camera=(), microphone=(), geolocation=()"
IP Allowlisting
labels:
- "traefik.http.routers.admin.middlewares=admin-ipallow"
- "traefik.http.middlewares.admin-ipallow.ipallowlist.sourcerange=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,YOUR.PUBLIC.IP/32"
Basic Authentication
labels:
- "traefik.http.routers.app.middlewares=app-auth"
- "traefik.http.middlewares.app-auth.basicauth.users=admin:$$2y$$10$$hash-here"
Redirect Regex
# Redirect www to non-www
labels:
- "traefik.http.routers.www-redirect.rule=Host(`www.yourdomain.com`)"
- "traefik.http.routers.www-redirect.middlewares=www-to-nonwww"
- "traefik.http.middlewares.www-to-nonwww.redirectregex.regex=^https://www\\.(.+)"
- "traefik.http.middlewares.www-to-nonwww.redirectregex.replacement=https://$${1}"
- "traefik.http.middlewares.www-to-nonwww.redirectregex.permanent=true"
Chaining Multiple Middleware
Apply multiple middleware to a single router by listing them comma-separated:
labels:
- "traefik.http.routers.app.middlewares=security-headers,api-ratelimit,compress-response"
- "traefik.http.middlewares.compress-response.compress=true"
Shared Middleware via File Provider
If you use the same middleware across many applications, define them once in a dynamic configuration file instead of repeating labels. Add a file provider to traefik.yml:
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: web
file:
filename: /etc/traefik/dynamic.yml
watch: true
Create the dynamic configuration:
sudo nano /opt/traefik/dynamic.yml
http:
middlewares:
default-security-headers:
headers:
stsSeconds: 63072000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
contentTypeNosniff: true
frameDeny: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
default-ratelimit:
rateLimit:
average: 100
burst: 50
period: 1m
default-compress:
compress: {}
Mount this file in the Traefik container by adding a volume:
volumes:
- ./dynamic.yml:/etc/traefik/dynamic.yml:ro
Then reference these middleware from any container's labels:
labels:
- "traefik.http.routers.app.middlewares=default-security-headers@file,default-ratelimit@file,default-compress@file"
Production Security Hardening
Docker Socket Security
Traefik needs access to the Docker socket to discover containers, but the Docker socket grants root-level control over the host. Mitigate this risk:
Option 1: Read-only mount (already done in our compose file):
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Option 2: Docker socket proxy (more secure — recommended for production):
sudo nano /opt/traefik/docker-compose.yml
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: socket-proxy
restart: unless-stopped
environment:
CONTAINERS: 1
SERVICES: 0
TASKS: 0
NETWORKS: 1
NODES: 0
SWARM: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket-proxy
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
depends_on:
- socket-proxy
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- ./traefik.yml:/traefik.yml:ro
- ./dynamic.yml:/etc/traefik/dynamic.yml:ro
- ./letsencrypt:/letsencrypt
- /var/log/traefik:/var/log/traefik
networks:
- web
- socket-proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$your-hash"
networks:
web:
external: true
socket-proxy:
internal: true
Update traefik.yml to use the socket proxy:
providers:
docker:
endpoint: "tcp://socket-proxy:2375"
exposedByDefault: false
network: web
The socket proxy only exposes the container listing API — it blocks dangerous endpoints like exec, volumes, and network management.
TLS Configuration
Harden TLS settings in the dynamic configuration file:
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
sniStrict: true
Container Security Best Practices
- Always use
security_opt: - no-new-privileges:trueon all containers - Run application containers as non-root users when possible
- Use read-only file system mounts where applicable (
:ro) - Keep database containers on internal networks, not exposed to Traefik
- Pin Docker image versions (e.g.,
traefik:v3.3instead oftraefik:latest) - Set resource limits on containers to prevent runaway processes
For more Docker security recommendations, refer to our security hardening guide.
Health Checks and High Availability
Add Docker health checks to your containers so Traefik can route traffic only to healthy instances:
services:
api:
image: node:22-alpine
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
Traefik respects Docker health checks. If a container becomes unhealthy, Traefik stops routing traffic to it until it recovers.
For true high availability with automatic failover, consider deploying on MassiveGRID Dedicated VPS with guaranteed CPU resources, ensuring your Traefik instance and application containers have consistent performance under load.
Troubleshooting
Container Not Appearing in Traefik
Check these common causes:
- Missing
traefik.enable=truelabel — required becauseexposedByDefaultis false - Container not on the
webnetwork — Traefik can only route to containers on its network - Container not running — verify with
docker ps - Port not specified — if the container exposes multiple ports, you must set
loadbalancer.server.port
# Check Traefik logs for errors
docker logs traefik --tail 50
# Verify container labels
docker inspect wordpress --format '{{json .Config.Labels}}' | jq .
Certificate Not Issued
- Verify DNS A record points to your server:
dig +short A yourdomain.com - Ensure port 80 is open (HTTP-01 challenge requires it)
- Check
acme.jsonpermissions: must be 600 - Check Traefik logs for ACME errors:
docker logs traefik 2>&1 | grep -i acme
502 Bad Gateway
This means Traefik can reach the router but cannot connect to the backend service:
- Verify the container is running and healthy
- Check the
loadbalancer.server.portmatches the container's listening port - Ensure the container is on the same Docker network as Traefik
Backing Up Traefik
The critical file to back up is acme.json, which contains all your SSL certificates and ACME account keys:
cp /opt/traefik/letsencrypt/acme.json /var/backups/traefik-acme-$(date +%Y%m%d).json
Also back up your configuration files:
tar czf /var/backups/traefik-config-$(date +%Y%m%d).tar.gz \
/opt/traefik/traefik.yml \
/opt/traefik/dynamic.yml \
/opt/traefik/docker-compose.yml
Automate this with a cron job (see our cron jobs guide) or include it in your server backup strategy (see our automatic backups guide).
Prefer Managed Infrastructure?
If managing Docker, Traefik, SSL certificates, security hardening, monitoring, and backups is more infrastructure work than your team wants to handle, consider MassiveGRID's Managed Dedicated Cloud Servers. The managed service handles the entire infrastructure layer — container orchestration, reverse proxy configuration, SSL management, security patches, monitoring, and 24/7 incident response — while you focus on building your applications. Every managed server runs on a Proxmox HA cluster with automatic failover and Ceph triple-replicated NVMe storage.
What's Next
- Docker installation guide — Docker and Docker Compose setup on Ubuntu
- Let's Encrypt SSL guide — certificate management beyond Traefik
- Nginx reverse proxy guide — alternative reverse proxy for non-Docker setups
- VPS monitoring setup — Prometheus, Grafana, and alerting
- Automatic backups guide — backup strategies for Docker volumes and data
- Security hardening guide — firewall, SSH hardening, fail2ban