Install Traefik Reverse Proxy on VPS Docker

Install Traefik Reverse Proxy on VPS Docker

June 2026 · 8 min read

Traefik is a modern reverse proxy designed specifically for containerized environments. Its key advantages are automatic Docker label detection, SSL acquisition from Let's Encrypt without setting up Certbot separately, and configuration reloads without service restarts. It's ideal for VPS deployments running multiple services simultaneously.

Prepare Docker Network

# Create a shared network for Traefik
docker network create traefik-public

# Verify Docker is installed
docker --version
docker compose version

Directory Structure

mkdir -p ~/traefik/config ~/traefik/certs
cd ~/traefik

# Create acme.json to store SSL certificates
touch certs/acme.json
chmod 600 certs/acme.json

Create traefik.yml (Static Config)

cat > config/traefik.yml << 'EOF'
api:
  dashboard: true
  insecure: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: /certs/acme.json
      tlsChallenge: {}

providers:
  docker:
    exposedByDefault: false
    network: traefik-public
  file:
    directory: /config/dynamic
    watch: true
EOF

Create docker-compose.yml for Traefik

cat > docker-compose.yml << 'EOF'
version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config/traefik.yml:/traefik.yml:ro
      - ./config/dynamic:/config/dynamic:ro
      - ./certs/acme.json:/certs/acme.json
    networks:
      - traefik-public
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(\`traefik.yourdomain.com\`)"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$hash"

networks:
  traefik-public:
    external: true
EOF

docker compose up -d

Add Services for Traefik to Route

# Example docker-compose.yml for your application
services:
  myapp:
    image: nginx:alpine
    networks:
      - traefik-public
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myapp.rule=Host(\`app.yourdomain.com\`)"
      - "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.myapp.loadbalancer.server.port=80"

networks:
  traefik-public:
    external: true
Tip: Each time you start a new container with Traefik labels, Traefik automatically detects and routes traffic instantly without any reload required.

Set Up Additional Middleware

# Dynamic config: config/dynamic/middlewares.yml
http:
  middlewares:
    security-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        contentTypeNosniff: true
        browserXssFilter: true
        referrerPolicy: "strict-origin-when-cross-origin"

    rate-limit:
      rateLimit:
        average: 100
        burst: 50

Verify Traefik is Working Correctly

# Check container status
docker compose ps

# View logs
docker compose logs traefik -f

# Verify ports 80/443 are open
ss -tlnp | grep -E '80|443'

# Test HTTP redirect
curl -I http://yourdomain.com
Tip: Access the Traefik Dashboard at https://traefik.yourdomain.com to view all active routers, services, and middlewares in real-time.

Common Issues & Troubleshooting

Error: "acme.json has wrong permissions"

The acme.json file must have permission 600 only, otherwise Traefik will refuse to start:

chmod 600 ~/traefik/certs/acme.json
docker compose restart traefik

SSL Certificate Not Being Issued (Stuck at ACME Challenge)

Port 80 must be publicly open for HTTP challenge. Check your firewall:

# Check UFW status
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# View Traefik logs for ACME errors
docker logs traefik 2>&1 | grep -i "acme\|cert\|error"

New Container Not Being Routed

You may have forgotten the traefik.enable=true label or the network doesn't match. Check:

# View container labels
docker inspect myapp | grep -A 20 Labels

# Verify container is in traefik-public network
docker network inspect traefik-public

Dashboard Access Denied (401 Unauthorized)

Generate a proper basic auth hash:

# Generate hash for user admin with password=mypassword
echo $(htpasswd -nb admin mypassword) | sed -e s/\\$/\\$\\$/g
# Use the output in the basicauth.users label

Frequently Asked Questions

How is Traefik different from Nginx Proxy Manager?

Nginx Proxy Manager has a web UI that's easy for beginners, but Traefik integrates with Docker labels automatically. It's ideal for DevOps teams managing infrastructure as code — no need to click through the UI every time you add a service.

Does Traefik support TCP (non-HTTP) routing?

Yes, Traefik supports TCP and UDP routing through custom EntryPoints. You can route MySQL port 3306 or game server UDP ports without needing a separate Nginx instance.

Can I use Traefik with Cloudflare?

Absolutely. Use Cloudflare DNS challenge instead of HTTP challenge to obtain wildcard SSL certificates, and Cloudflare provides DDoS protection before traffic reaches Traefik. You can also set trustedIPs so Traefik sees visitor real IPs.