Traefik Reverse Proxy: The Complete Self-Hosting Guide for HTTPS and SSL Automation

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:

ServiceManaged SolutionSelf-HostedAnnual Savings
Load Balancer$25/month€5-8/month$200+
SSL Certificates$10/monthFree$120
Multiple domains$5/domain/monthFree$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.

Scroll to Top