Self-hosting multiple applications becomes exponentially more powerful when you can run them under different domains with automatic HTTPS. Traefik is the modern reverse proxy that makes this possible, eliminating the complexity of manual SSL certificate management while providing production-grade routing capabilities. If you’ve ever struggled with Nginx configurations or Let’s Encrypt renewals, Traefik will revolutionize your self-hosting workflow.
What You’ll Build
By the end of this comprehensive guide, you’ll have:
- ✅ Traefik reverse proxy handling all HTTP/HTTPS traffic
- ✅ Automatic SSL certificates via Let’s Encrypt with zero maintenance
- ✅ Multiple services running under different domains/subdomains
- ✅ Production-ready configuration that scales from hobby to enterprise
- ✅ Advanced routing rules for complex application architectures
- ✅ Container auto-discovery that eliminates manual configuration
- ✅ Security hardening with proper headers and redirects
Why Traefik Over Traditional Reverse Proxies
The Traditional Pain Points
Nginx/Apache challenges:
- Manual SSL certificate management and renewals
- Complex configuration files that break easily
- No automatic service discovery
- Separate configuration for each new service
- Manual load balancer updates
Traefik advantages:
- Automatic service discovery: Containers register themselves
- Zero-downtime SSL: Let’s Encrypt integration with auto-renewal
- Dynamic configuration: No restarts when adding services
- Docker-native: Built specifically for containerized environments
- Modern protocols: HTTP/2, WebSocket, gRPC support out of the box
Cost and Efficiency Benefits
Self-hosting with Traefik vs. managed solutions:
- Cloudflare Pro: $20/month per domain
- AWS Application Load Balancer: $16-25/month + data transfer
- Self-hosted Traefik: €5-8/month (unlimited domains)
- Annual savings: $150-250+ depending on scale
Prerequisites
- Cloud server (any provider: DigitalOcean, Vultr, Linode, AWS, etc.)
- Domain name with DNS control
- Basic Docker knowledge
- SSH access to your server
Step 1: Server Preparation
Create Your Cloud Server
Recommended cloud server specifications:
- Image: Ubuntu 24.04 LTS
- Type: Small instance (1 vCPU, 4 GB RAM) – around €5-8/month
- Location: Choose closest to your users
- SSH Key: Add your public key for secure access
Initial Server Setup
Connect to your server:
ssh root@YOUR_SERVER_IP
Update the system and install Docker:
# System updates
apt update && apt upgrade -y
# Install Docker dependencies
apt install apt-transport-https ca-certificates curl software-properties-common gnupg -y
# Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
apt update
apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
# Create Docker network for Traefik
docker network create proxy
Verify installation:
docker --version
docker compose version
Step 2: Core Traefik Configuration
Directory Structure Setup
Create organized directories for your Traefik installation:
mkdir -p /opt/traefik/data
cd /opt/traefik
Main Traefik Configuration
Create the main configuration file:
nano /opt/traefik/data/traefik.yml
Add this production-ready configuration:
# Global configuration
global:
checkNewVersion: false
sendAnonymousUsage: false
# API configuration (disabled for security)
api:
dashboard: false
insecure: false
# Entry points define which ports Traefik listens on
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
permanent: true
https:
address: ":443"
# Provider configuration - tells Traefik where to find services
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: proxy
watch: true
# Certificate management with Let's Encrypt
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com # CHANGE THIS!
storage: acme.json
httpChallenge:
entryPoint: http
# Uncomment for staging (testing) certificates
# caServer: https://acme-staging-v02.api.letsencrypt.org/directory
# Logging configuration
log:
level: INFO
filePath: "/var/log/traefik.log"
accessLog:
filePath: "/var/log/access.log"
# Metrics (optional but recommended)
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
SSL Certificate Storage
Create the certificate storage file:
touch /opt/traefik/data/acme.json
chmod 600 /opt/traefik/data/acme.json
Docker Compose Configuration
Create the Traefik Docker Compose file:
nano /opt/traefik/docker-compose.yml
Add this configuration:
version: '3.8'
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
networks:
- proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/etc/traefik/traefik.yml:ro
- ./data/acme.json:/acme.json
- ./logs:/var/log
environment:
- TZ=Europe/Berlin # Change to your timezone
labels:
- "traefik.enable=true"
# Optional: Traefik dashboard (enable only if needed)
# - "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
# - "traefik.http.routers.dashboard.entrypoints=https"
# - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# - "traefik.http.routers.dashboard.service=api@internal"
networks:
proxy:
external: true
Create Log Directory
mkdir -p /opt/traefik/logs
Step 3: Testing with a Simple Application
Before deploying complex applications, let’s test Traefik with a simple web service.
Create Test Service
mkdir -p /opt/whoami
cd /opt/whoami
Create a test service Docker Compose file:
nano /opt/whoami/docker-compose.yml
version: '3.8'
services:
whoami:
image: traefik/whoami:latest
container_name: whoami
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`test.yourdomain.com`)" # CHANGE THIS!
- "traefik.http.routers.whoami.entrypoints=https"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
proxy:
external: true
Start Services
Start Traefik first:
cd /opt/traefik
docker compose up -d
Check Traefik logs:
docker logs traefik
Start the test service:
cd /opt/whoami
docker compose up -d
DNS Configuration
Point your test subdomain to your server:
- Go to your DNS provider
- Create an A record:
- Name: test
- Type: A
- Value: YOUR_SERVER_IP
- TTL: 300
Wait for DNS propagation (usually 5-15 minutes).
Test Your Setup
Visit https://test.yourdomain.com
– you should see:
- ✅ Green lock icon (HTTPS working)
- ✅ Information about the container serving the request
- ✅ No certificate warnings
Step 4: Real-World Application Examples
Example 1: WordPress with Database
mkdir -p /opt/wordpress
cd /opt/wordpress
nano docker-compose.yml
version: '3.8'
services:
wordpress:
image: wordpress:latest
container_name: wordpress
restart: unless-stopped
networks:
- proxy
- wordpress-internal
environment:
- WORDPRESS_DB_HOST=wordpress-db
- WORDPRESS_DB_NAME=wordpress
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=secure_password_here
volumes:
- wordpress_data:/var/www/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.rule=Host(`blog.yourdomain.com`)"
- "traefik.http.routers.wordpress.entrypoints=https"
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
wordpress-db:
image: mariadb:latest
container_name: wordpress-db
restart: unless-stopped
networks:
- wordpress-internal
environment:
- MYSQL_ROOT_PASSWORD=root_password_here
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=secure_password_here
volumes:
- wordpress_db:/var/lib/mysql
volumes:
wordpress_data:
wordpress_db:
networks:
proxy:
external: true
wordpress-internal:
internal: true
Example 2: Multiple Subdomains
mkdir -p /opt/multisite
cd /opt/multisite
nano docker-compose.yml
version: '3.8'
services:
# Main application
app:
image: nginx:alpine
container_name: main-app
restart: unless-stopped
networks:
- proxy
volumes:
- ./html:/usr/share/nginx/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.yourdomain.com`)"
- "traefik.http.routers.app.entrypoints=https"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
# API service
api:
image: nginx:alpine
container_name: api-service
restart: unless-stopped
networks:
- proxy
volumes:
- ./api:/usr/share/nginx/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.yourdomain.com`)"
- "traefik.http.routers.api.entrypoints=https"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
# Admin panel
admin:
image: nginx:alpine
container_name: admin-panel
restart: unless-stopped
networks:
- proxy
volumes:
- ./admin:/usr/share/nginx/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.admin.rule=Host(`admin.yourdomain.com`)"
- "traefik.http.routers.admin.entrypoints=https"
- "traefik.http.routers.admin.tls.certresolver=letsencrypt"
networks:
proxy:
external: true
Step 5: Advanced Routing and Middleware
Path-Based Routing
Route different paths to different services:
labels:
- "traefik.enable=true"
# Main app gets everything
- "traefik.http.routers.app-main.rule=Host(`yourdomain.com`) && PathPrefix(`/`)"
- "traefik.http.routers.app-main.priority=1"
# API gets /api paths
- "traefik.http.routers.app-api.rule=Host(`yourdomain.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.app-api.priority=10"
- "traefik.http.routers.app-api.middlewares=api-stripprefix"
# Strip /api prefix before forwarding
- "traefik.http.middlewares.api-stripprefix.stripprefix.prefixes=/api"
Security Middleware
Add security headers and authentication:
labels:
- "traefik.enable=true"
- "traefik.http.routers.secure-app.rule=Host(`secure.yourdomain.com`)"
- "traefik.http.routers.secure-app.entrypoints=https"
- "traefik.http.routers.secure-app.tls.certresolver=letsencrypt"
- "traefik.http.routers.secure-app.middlewares=security-headers,basic-auth"
# Security headers
- "traefik.http.middlewares.security-headers.headers.frameDeny=true"
- "traefik.http.middlewares.security-headers.headers.sslRedirect=true"
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
# Basic authentication (generate with: htpasswd -nb username password)
- "traefik.http.middlewares.basic-auth.basicauth.users=admin:$$2y$$10$$..."
Rate Limiting
Protect your services from abuse:
labels:
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.rate-limit.ratelimit.period=1m"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
- "traefik.http.routers.app.middlewares=rate-limit"
Step 6: Systemd Integration and Auto-Start
Create Systemd Service
nano /etc/systemd/system/traefik-docker.service
[Unit]
Description=Traefik Docker Compose
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/traefik
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Enable Auto-Start
systemctl daemon-reload
systemctl enable traefik-docker.service
systemctl start traefik-docker.service
Verify Service
systemctl status traefik-docker.service
docker ps | grep traefik
Step 7: Monitoring and Logging
Enable Prometheus Metrics
Add to your traefik.yml
:
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
addRoutersLabels: true
Access metrics at: http://YOUR_SERVER_IP:8080/metrics
Log Analysis
Monitor Traefik activity:
# Real-time logs
docker logs -f traefik
# Access logs
tail -f /opt/traefik/logs/access.log
# Error patterns
grep -i error /opt/traefik/logs/traefik.log
Health Checks
Create a monitoring script:
nano /opt/scripts/traefik-health.sh
#!/bin/bash
# Check if Traefik container is running
if ! docker ps | grep -q traefik; then
echo "ERROR: Traefik container not running"
exit 1
fi
# Check if Traefik is responding
if ! curl -f -s http://localhost:80 > /dev/null; then
echo "ERROR: Traefik not responding on port 80"
exit 1
fi
# Check certificate expiry (basic check)
if [ -s /opt/traefik/data/acme.json ]; then
echo "OK: Certificates present"
else
echo "WARNING: No certificates found"
fi
echo "OK: Traefik health check passed"
chmod +x /opt/scripts/traefik-health.sh
Step 8: Backup and Disaster Recovery
Automated Backup Script
nano /opt/scripts/backup-traefik.sh
#!/bin/bash
BACKUP_DIR="/opt/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup Traefik configuration
tar -czf $BACKUP_DIR/traefik-config-$DATE.tar.gz \
/opt/traefik/data/traefik.yml \
/opt/traefik/data/acme.json \
/opt/traefik/docker-compose.yml
# Backup all service configurations
tar -czf $BACKUP_DIR/services-config-$DATE.tar.gz \
/opt/*/docker-compose.yml \
--exclude=/opt/backups
# Keep only last 30 days of backups
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: $BACKUP_DIR/traefik-config-$DATE.tar.gz"
chmod +x /opt/scripts/backup-traefik.sh
Schedule Backups
crontab -e
Add daily backup at 2 AM:
0 2 * * * /opt/scripts/backup-traefik.sh
Restore Procedure
# Stop Traefik
systemctl stop traefik-docker.service
# Restore configuration
cd /opt
tar -xzf /opt/backups/traefik-config-YYYYMMDD_HHMMSS.tar.gz
# Fix permissions
chmod 600 /opt/traefik/data/acme.json
# Restart Traefik
systemctl start traefik-docker.service
Troubleshooting Common Issues
SSL Certificate Problems
Issue: Certificate not generated
# Check Traefik logs
docker logs traefik | grep -i acme
# Common causes:
# 1. DNS not pointing to server
# 2. Port 80 blocked
# 3. Wrong email in configuration
# 4. Rate limits (5 certificates per domain per week)
Solution: Use staging certificates first:
certificatesResolvers:
letsencrypt:
acme:
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
Service Not Accessible
Issue: Service returns 404 or 502
# Check if container is in proxy network
docker inspect CONTAINER_NAME | grep -A 10 Networks
# Check Traefik routing
docker logs traefik | grep -i "router"
# Verify labels
docker inspect CONTAINER_NAME | grep -A 20 Labels
Complex Traefik configurations can involve intricate routing rules, middleware configurations, and certificate management across multiple services. If you’re experiencing persistent routing issues or need help optimizing your Traefik deployment for production use, don’t waste time troubleshooting alone – contact our infrastructure team for expert guidance.
Performance Issues
Issue: Slow response times
# Check resource usage
docker stats traefik
# Increase worker processes in traefik.yml
global:
maxIdleConnsPerHost: 200
# Enable HTTP/2
entryPoints:
https:
address: ":443"
http2:
maxConcurrentStreams: 250
Memory Issues
Issue: Traefik consuming too much memory
# Add memory limits to docker-compose.yml
services:
traefik:
# ... existing config
deploy:
resources:
limits:
memory: 128M
reservations:
memory: 64M
Security Best Practices
Firewall Configuration
# Install UFW
apt install ufw
# Default policies
ufw default deny incoming
ufw default allow outgoing
# Allow SSH (change port if needed)
ufw allow 22
# Allow HTTP/HTTPS only
ufw allow 80
ufw allow 443
# Enable firewall
ufw enable
Container Security
# Run containers as non-root user where possible
# Add to docker-compose.yml:
services:
app:
user: "1000:1000" # Use appropriate UID:GID
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
Regular Security Updates
# Create update script
nano /opt/scripts/update-system.sh
#!/bin/bash
# Update system packages
apt update && apt upgrade -y
# Update Docker images
cd /opt/traefik && docker compose pull && docker compose up -d
cd /opt/whoami && docker compose pull && docker compose up -d
# Clean up old images
docker system prune -f
# Check for failed services
systemctl --failed
IP Whitelisting
For admin interfaces, restrict access by IP:
labels:
- "traefik.http.middlewares.admin-whitelist.ipwhitelist.sourcerange=192.168.1.0/24,10.0.0.0/8"
- "traefik.http.routers.admin.middlewares=admin-whitelist"
Performance Optimization
HTTP/2 and HTTP/3
Enable modern protocols:
# In traefik.yml
entryPoints:
https:
address: ":443"
http2:
maxConcurrentStreams: 250
# Experimental HTTP/3 support
http3: {}
Compression
Enable gzip compression:
# Add middleware for compression
labels:
- "traefik.http.middlewares.gzip.compress=true"
- "traefik.http.routers.app.middlewares=gzip"
Caching Headers
Add caching for static assets:
labels:
- "traefik.http.middlewares.cache-headers.headers.customresponseheaders.Cache-Control=public, max-age=31536000"
- "traefik.http.routers.static.middlewares=cache-headers"
Cost Analysis and ROI
Self-Hosting vs Managed Services
Monthly cost comparison:
Service | Managed Solution | Self-Hosted | Annual Savings |
---|---|---|---|
Load Balancer | $25/month | €5-8/month | $200+ |
SSL Certificates | $10/month | Free | $120 |
Multiple domains | $5/domain/month | Free | $300+ |
Total | $40+/month | €5-8/month | $350+ |
Break-even Analysis
- Initial setup time: 4-6 hours
- Monthly maintenance: 1-2 hours
- Learning curve: 2-3 days for complete proficiency
- ROI: Positive within 30-60 days for most use cases
Scaling Your Infrastructure
Multiple Server Setup
For high availability:
# Load balance between multiple backends
labels:
- "traefik.http.services.app.loadbalancer.server.port=80"
- "traefik.http.services.app.loadbalancer.healthcheck.path=/health"
- "traefik.http.services.app.loadbalancer.healthcheck.interval=30s"
Container Orchestration
Migrate to Docker Swarm or Kubernetes when ready:
# Docker Swarm mode
version: '3.8'
services:
traefik:
image: traefik:v3.0
deploy:
replicas: 2
placement:
constraints:
- node.role == manager
Enterprise-grade scaling with Traefik involves complex load balancing strategies, health check configurations, and high-availability setups across multiple servers. Planning significant infrastructure growth? Let’s discuss your scaling requirements – our team specializes in designing robust, future-proof infrastructure architectures.
Database Clustering
For mission-critical applications:
# MariaDB Galera cluster example
services:
db-cluster:
image: mariadb:latest
deploy:
replicas: 3
environment:
- MYSQL_INITDB_SKIP_TZINFO=1
- MYSQL_ROOT_PASSWORD=secure_password
Professional Infrastructure Support
Self-hosting with Traefik offers incredible flexibility and cost savings, but implementing production-grade reverse proxy infrastructure involves careful consideration of security, performance, and scalability requirements. Why struggle with complex configurations when expert help is available?
Complex scenarios where professional support accelerates success:
- Multi-server load balancing and high availability configurations
- Advanced security implementations with WAF and DDoS protection
- Performance optimization for high-traffic applications
- Compliance requirements for regulated industries
- Custom middleware development and advanced routing logic
- Integration with existing enterprise infrastructure
Ready to Implement Professional-Grade Infrastructure?
Stop spending weeks troubleshooting configurations that experts can implement in days. Whether you’re building on our n8n self-hosting foundation or implementing entirely new infrastructure, professional guidance ensures optimal results from day one.
Contact our infrastructure specialists today – we work with organizations of all sizes to implement robust, scalable reverse proxy solutions. From initial setup through advanced enterprise deployments, we’ll ensure your self-hosting infrastructure meets both current needs and ambitious growth plans.
Conclusion
Traefik transforms self-hosting from a complex undertaking into an elegant, automated process. With automatic SSL certificate management, service discovery, and powerful routing capabilities, you can run enterprise-grade infrastructure at a fraction of traditional costs.
This tutorial builds perfectly on our complete n8n self-hosting guide – if you followed that tutorial, you already have Traefik running and can now expand your infrastructure to host multiple applications with the same reverse proxy setup.
Key benefits of this setup:
- Cost-effective: Save hundreds annually compared to managed solutions
- Zero-maintenance SSL: Automatic certificate generation and renewal
- Infinite scalability: Add services without configuration changes
- Production-ready: Battle-tested configuration used in real environments
- Modern protocols: HTTP/2, WebSocket, and gRPC support included
This configuration has been refined through real-world deployments and provides the foundation for everything from personal projects to business-critical applications.
The combination of Traefik’s automation capabilities with proper monitoring, security, and backup procedures creates a robust self-hosting platform that rivals expensive managed services while maintaining complete control over your infrastructure.
About tva
tva ensures comprehensive infrastructure management of database systems, cloud environments, and global supply chains. Our methodical approach combines rigorous security protocols with performance optimization, while strategic advisory services enable precise coordination of both digital capabilities and physical assets – maintaining the highest standards of operational excellence and compliance throughout all engagements.
Visit tva.sg for more information about our infrastructure management services and additional self-hosting tutorials.