Traefik Reverse Proxy: Automatic HTTPS for Your Homelab
Deploy Traefik with automatic SSL certificates and Docker service discovery. Secure your homelab services the smart way.
You’re running 5+ containerized services in your homelab. Each needs its own domain, SSL certificate, and authentication. You’re spending more time managing Nginx configs than actually enjoying your setup. Sound familiar?
Here’s the good news: Traefik eliminates this complexity entirely. Automatic SSL certificates that renew themselves. Dynamic service discovery through Docker labels. Infrastructure-as-code configuration that scales from 3 services to 30+ without breaking a sweat.
In this guide, you’ll deploy Traefik from scratch and learn how to secure your homelab services with automatic HTTPS, centralized authentication, and zero-touch certificate management. No more manual cert juggling. No more restarting your reverse proxy every time you add a service.
Let’s build something that actually works for you instead of against you.
Why Traefik? (And Why Now?)
Traditional reverse proxies like Nginx were designed for static infrastructure - the kind where servers are named, numbered, and stay put for years. That’s not your homelab. You’re constantly spinning up new services, testing configurations, and iterating on your setup.
Traefik was built specifically for dynamic environments. Here’s what makes it special:
- Auto-discovery: Add a few Docker labels to your service, and Traefik instantly routes traffic to it
- Automatic HTTPS: Let’s Encrypt certificates are requested, renewed, and managed without you lifting a finger
- Zero restarts: Deploy a new service, and it’s immediately accessible - Traefik updates its configuration on the fly
- Infrastructure as Code: All configuration lives in version control, making your setup reproducible and auditable
- Built-in security: Rate limiting, authentication middleware, and security headers are first-class features
The AI angle: Traefik’s label-based configuration is perfect for automation. AI agents can generate complete service configurations, analyze logs for issues, and even suggest security improvements based on your traffic patterns. We’ll explore these opportunities throughout this guide.
Prerequisites
Before diving in, make sure you have:
Technical Knowledge:
- Basic Docker and Docker Compose familiarity
- Understanding of DNS and domain management
- Ability to edit YAML configuration files
- Network basics (ports, protocols, firewall rules)
Infrastructure:
- Docker engine (v20.10+) or Docker Desktop
- 512MB+ RAM for Traefik container (typically uses ~100MB)
- 100MB+ disk space for configuration and certificates
- Internet access for Let’s Encrypt certificate validation
- A registered domain (or ability to create wildcard DNS)
Accounts & API Keys:
- DNS provider account (Cloudflare recommended)
- Email address for Let’s Encrypt account
- Optional: Cloudflare API token for DNS challenge (we’ll create this together)
Time Commitment: 45 minutes for basic setup, up to 2 hours if you want full security hardening.
Ready? Let’s go.
Understanding Traefik’s Core Concepts
Before we start deploying containers, let’s understand how Traefik thinks about the world. This 5-minute conceptual overview will save you hours of troubleshooting later.
The Problem Without a Reverse Proxy
Imagine your homelab without Traefik:
- Plex runs on
192.168.1.100:32400 - Jellyfin runs on
192.168.1.100:8096 - Home Assistant runs on
192.168.1.100:8123
To access each service, you need to remember different ports, manage separate SSL certificates, and configure authentication individually. It’s chaos.
The Solution: Traefik’s Architecture
With Traefik, all requests flow through a single entry point:
plex.home.example.com→ Routes to Plex (automatic HTTPS)jellyfin.home.example.com→ Routes to Jellyfin (automatic HTTPS)ha.home.example.com→ Routes to Home Assistant (automatic HTTPS)
One wildcard certificate. One authentication system. Zero manual configuration.
Traefik’s Six Core Components
1. Entry Points - Where Traefik listens for incoming requests
- Typically port 80 (HTTP) and 443 (HTTPS)
- Each entry point can have different rules and behaviors
- Example: Redirect all HTTP traffic to HTTPS automatically
2. Routers - Rules that analyze requests and decide where to send them
- Match based on hostname:
Host('plex.home.example.com') - Match based on path:
PathPrefix('/api') - Match based on headers, methods, query parameters
- Apply middleware in sequence before forwarding
3. Services - Your actual backend applications
- Define how to reach your applications (IP, port, protocol)
- Handle load balancing across multiple instances
- Configure health checks and sticky sessions
- Connection pooling for better performance
4. Middlewares - Intercept and transform requests/responses
- Authentication: BasicAuth, OAuth2, Forward Auth
- Rate Limiting: Throttle requests to prevent abuse
- Headers: Add security headers, CORS configuration
- Compression: gzip/brotli for faster transfers
- Redirects: HTTP→HTTPS, URL rewriting
5. Providers - Sources where Traefik learns about your services
- Docker: Auto-discover containers via labels (most common for homelabs)
- File: Static YAML configuration files
- Kubernetes: Native K8s ingress integration
- The magic happens here - when a container starts, the Docker provider reads its labels and automatically configures routing
6. Certificate Resolvers - Manage automatic SSL certificates
- Let’s Encrypt integration (free SSL certificates)
- HTTP-01 challenge: Prove domain ownership via HTTP
- DNS-01 challenge: Prove domain ownership via DNS (required for wildcards)
- Automatic renewal 30 days before expiration
How They Work Together
Here’s what happens when someone visits https://plex.home.example.com:
1. Request arrives at Entry Point (port 443)
↓
2. Router matches Host('plex.home.example.com')
↓
3. Middlewares run (Auth → Rate Limit → Security Headers)
↓
4. Service forwards to backend (plex:32400)
↓
5. Response flows back through the same chain
↓
6. Client receives encrypted, authenticated response The entire flow is automatic. You just told Traefik (via Docker labels) that the Plex container exists and what domain it should respond to. Traefik handles the rest.
Pro tip: Think of Traefik as a smart traffic cop. It knows where everyone is going, ensures they have proper credentials, and directs them to the right destination - all while keeping everyone secure with HTTPS.
Installing Traefik with Docker Compose
Time to get hands-on. We’ll use Docker Compose because it’s clean, reproducible, and easy to version control.
Step 1: Create Directory Structure
# Create Traefik directory
mkdir -p ~/homelab/traefik
cd ~/homelab/traefik
# Create configuration files
touch docker-compose.yml
touch traefik.yml
touch dynamic.yml
touch .env
# Create certificate storage with correct permissions
touch acme.json
chmod 600 acme.json Why chmod 600? Traefik requires strict permissions on the certificate file for security. Only the owner can read/write.
Step 2: Create Static Configuration (traefik.yml)
This file defines Traefik’s global behavior - things that rarely change.
# ~/homelab/traefik/traefik.yml
global:
checkNewVersion: false
sendAnonymousUsage: false
# Enable API and dashboard
api:
dashboard: true
debug: false
# Define entry points (where Traefik listens)
entryPoints:
web:
address: ":80"
# Redirect all HTTP to HTTPS
http:
redirections:
entrypoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
domains:
- main: home.example.com
sans:
- "*.home.example.com"
# Let's Encrypt certificate management
certificatesResolvers:
letsencrypt:
acme:
email: [email protected] # Change this to your email
storage: acme.json
# Use DNS challenge for wildcard certificates
dnsChallenge:
provider: cloudflare
resolvers:
- 1.1.1.1:53
- 8.8.8.8:53
# Configure providers
providers:
docker:
endpoint: unix:///var/run/docker.sock
exposedByDefault: false # Services hidden unless labeled
defaultRule: Host(`[[ .ContainerName ]].home.example.com`)
network: traefik
watch: true
file:
filename: /dynamic.yml
watch: true
# Enable Prometheus metrics
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
# Logging
log:
level: INFO
accessLog:
format: json What’s happening here?
- All HTTP traffic redirects to HTTPS automatically
- Let’s Encrypt handles certificate acquisition via Cloudflare DNS
- Docker containers are only exposed if they have
traefik.enable=true - Prometheus metrics are available for monitoring
- JSON access logs for easy parsing (perfect for AI analysis!)
Step 3: Create Dynamic Configuration (dynamic.yml)
This file holds settings that might change more frequently - TLS options, middleware definitions, etc.
# ~/homelab/traefik/dynamic.yml
# TLS configuration
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
sniStrict: true
# Define reusable middleware
http:
middlewares:
# Security headers middleware
security-headers:
headers:
sslRedirect: true
sslProxyHeaders:
X-Forwarded-Proto: https
customResponseHeaders:
X-Frame-Options: "SAMEORIGIN"
X-Content-Type-Options: "nosniff"
X-XSS-Protection: "1; mode=block"
Strict-Transport-Security: "max-age=31536000; includeSubDomains"
# Rate limiting middleware
rate-limit:
rateLimit:
average: 100
burst: 50
period: 60s Pro tip: These middleware definitions are reusable across all services. Define them once, apply them everywhere.
Step 4: Create Environment Variables (.env)
Store sensitive data here. Never commit this file to git!
# ~/homelab/traefik/.env
# Domain configuration
DOMAIN=home.example.com
# Let's Encrypt configuration
[email protected]
# Cloudflare API credentials
[email protected]
CF_API_KEY=your_cloudflare_api_token_here Getting your Cloudflare API token:
- Log into Cloudflare → My Profile → API Tokens
- Click “Create Token”
- Use “Edit zone DNS” template
- Scope it to your specific domain
- Copy the token to your
.envfile
Security note: Use a scoped API token, never your global API key. This limits damage if the token is compromised.
Step 5: Create Docker Compose File
Now we bring it all together:
# ~/homelab/traefik/docker-compose.yml
version: '3.8'
services:
traefik:
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik
ports:
- "80:80"
- "443:443"
environment:
- CF_API_EMAIL=${CF_API_EMAIL}
- CF_API_KEY=${CF_API_KEY}
volumes:
# Time synchronization
- /etc/localtime:/etc/localtime:ro
# Docker socket (read-only)
- /var/run/docker.sock:/var/run/docker.sock:ro
# Configuration files
- ./traefik.yml:/traefik.yml:ro
- ./dynamic.yml:/dynamic.yml:ro
# Certificate storage (read-write)
- ./acme.json:/acme.json
labels:
# Enable Traefik for itself (dashboard)
- "traefik.enable=true"
# Dashboard router
- "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# Dashboard authentication (change this!)
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/"
networks:
traefik:
name: traefik
driver: bridge Important: The default password is admin:admin. Change this immediately! Generate a new password hash:
# Install htpasswd (part of apache2-utils on Ubuntu)
sudo apt-get install apache2-utils
# Generate password hash
htpasswd -nb admin your_secure_password
# Output example: admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
# Double the dollar signs in docker-compose.yml: $$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/ Step 6: Create Traefik Network and Launch
# Create the network (only needed once)
docker network create traefik
# Start Traefik
docker-compose up -d
# Watch the logs
docker-compose logs -f traefik What you should see:
traefik | time="2025-12-16T10:00:00Z" level=info msg="Configuration loaded from file: /traefik.yml"
traefik | time="2025-12-16T10:00:01Z" level=info msg="Traefik version 3.6.0 built on 2025-12-01T00:00:00Z"
traefik | time="2025-12-16T10:00:01Z" level=info msg="Starting provider aggregator.ProviderAggregator"
traefik | time="2025-12-16T10:00:01Z" level=info msg="Starting provider *docker.Provider"
traefik | time="2025-12-16T10:00:02Z" level=info msg="Server configuration reloaded on :80"
traefik | time="2025-12-16T10:00:02Z" level=info msg="Server configuration reloaded on :443" If you see these messages, congratulations! Traefik is running.
Step 7: Verify Dashboard Access
Open your browser and navigate to https://traefik.home.example.com (replace with your actual domain).
You should:
- See a certificate warning (if using a local DNS) or a valid certificate (if using public DNS)
- Be prompted for username/password
- See the Traefik dashboard with your routers, services, and middlewares
Troubleshooting: If you can’t access the dashboard:
- Check DNS:
nslookup traefik.home.example.comshould resolve to your server - Check logs:
docker-compose logs traefik | grep -i error - Check network:
docker network inspect traefik
Deploying Your First Service with Traefik
Theory is great, but let’s see Traefik in action. We’ll deploy Jellyfin (an open-source media server) with automatic HTTPS.
Jellyfin with Automatic Service Discovery
Create a new docker-compose file for Jellyfin:
# ~/homelab/jellyfin/docker-compose.yml
version: '3.8'
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
networks:
- traefik
environment:
- TZ=America/New_York
volumes:
- ./config:/config
- ./cache:/cache
- /mnt/media:/media:ro # Your media library
labels:
# Enable Traefik for this service
- "traefik.enable=true"
# Define routing rule
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.home.example.com`)"
# Use HTTPS entry point
- "traefik.http.routers.jellyfin.entrypoints=websecure"
# Request Let's Encrypt certificate
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
# Define service port (Jellyfin listens on 8096)
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
# Apply security headers
- "traefik.http.routers.jellyfin.middlewares=security-headers"
networks:
traefik:
external: true Now launch Jellyfin:
cd ~/homelab/jellyfin
docker-compose up -d What just happened?
- Jellyfin container started on the
traefiknetwork - Traefik detected the new container via the Docker provider
- Traefik read the labels and created a router for
jellyfin.home.example.com - Traefik requested a Let’s Encrypt certificate via Cloudflare DNS challenge
- Within 10-15 seconds,
https://jellyfin.home.example.comis live with valid HTTPS
No manual configuration. No restarting Traefik. Pure magic.
Check the Traefik dashboard - you’ll see a new router for Jellyfin, complete with TLS certificate information.
Understanding the Labels
Let’s break down what each label does:
traefik.enable=true- “Hey Traefik, please route traffic to this container”traefik.http.routers.jellyfin.rule=...- “Match requests for jellyfin.home.example.com”traefik.http.routers.jellyfin.entrypoints=websecure- “Use the HTTPS entry point (port 443)”traefik.http.routers.jellyfin.tls.certresolver=letsencrypt- “Get a certificate from Let’s Encrypt”traefik.http.services.jellyfin.loadbalancer.server.port=8096- “The container listens on port 8096”traefik.http.routers.jellyfin.middlewares=security-headers- “Apply the security-headers middleware”
AI opportunity: Instead of writing these labels manually, an AI agent could generate them based on a simple prompt: “Deploy Jellyfin with HTTPS and security headers”. The AI knows the port (8096), the best practices (security headers), and can output a complete docker-compose file in seconds.
Adding Authentication to Services
Some services (like admin panels) should be protected with authentication. Traefik makes this trivial.
Pattern 1: Basic Authentication
Perfect for protecting admin interfaces like Portainer:
# ~/homelab/portainer/docker-compose.yml
version: '3.8'
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
networks:
- traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`admin.home.example.com`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
# Add authentication middleware
- "traefik.http.routers.portainer.middlewares=portainer-auth"
- "traefik.http.middlewares.portainer-auth.basicauth.users=admin:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/"
networks:
traefik:
external: true Now when you visit https://admin.home.example.com, you’ll be prompted for credentials before accessing Portainer.
Pattern 2: Rate Limiting
Protect against brute force attacks and DDoS:
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.home.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=8080"
# Apply rate limiting: 100 requests per minute, max 50 concurrent
- "traefik.http.routers.myapp.middlewares=rate-limit"
- "traefik.http.middlewares.rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"
- "traefik.http.middlewares.rate-limit.ratelimit.period=60s" Pattern 3: Path-Based Routing
Run multiple services on the same domain using different paths:
# API service at home.example.com/api
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`home.example.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=8080"
# Web app at home.example.com/
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`home.example.com`) && PathPrefix(`/`)"
- "traefik.http.routers.webapp.entrypoints=websecure"
- "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
- "traefik.http.services.webapp.loadbalancer.server.port=3000" AI-Assisted Homelab Management
Here’s where homelabbing.io’s AI-forward approach really shines. Let’s explore how AI can supercharge your Traefik deployment.
1. AI-Generated Service Configurations
The old way: Google for examples, copy-paste labels, fix syntax errors, troubleshoot for 30 minutes.
The AI way: Describe what you want in plain English.
Prompt example:
"Deploy Home Assistant with:
- Automatic HTTPS via Let's Encrypt
- Rate limiting (200 requests per minute)
- OAuth2 authentication via Authentik
- Security headers enabled
- Access only from 192.168.0.0/16 network" AI output:
version: '3.8'
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:latest
container_name: home-assistant
restart: unless-stopped
networks:
- traefik
environment:
- TZ=America/New_York
volumes:
- ./config:/config
labels:
- "traefik.enable=true"
- "traefik.http.routers.ha.rule=Host(`ha.home.example.com`)"
- "traefik.http.routers.ha.entrypoints=websecure"
- "traefik.http.routers.ha.tls.certresolver=letsencrypt"
- "traefik.http.services.ha.loadbalancer.server.port=8123"
# Apply middleware chain: IP whitelist → OAuth2 → Rate limit → Security headers
- "traefik.http.routers.ha.middlewares=ha-ipwhitelist,ha-oauth,ha-ratelimit,security-headers"
# IP whitelist middleware (local network only)
- "traefik.http.middlewares.ha-ipwhitelist.ipwhitelist.sourcerange=192.168.0.0/16"
# OAuth2 forward auth
- "traefik.http.middlewares.ha-oauth.forwardauth.address=http://authentik:9000/outpost.goauthens/auth/traefik"
- "traefik.http.middlewares.ha-oauth.forwardauth.trustforwardheader=true"
# Rate limiting
- "traefik.http.middlewares.ha-ratelimit.ratelimit.average=200"
- "traefik.http.middlewares.ha-ratelimit.ratelimit.burst=100"
- "traefik.http.middlewares.ha-ratelimit.ratelimit.period=60s"
networks:
traefik:
external: true Time saved: 20-30 minutes per service. The AI knows the correct syntax, best practices, and common middleware patterns.
2. AI Log Analysis and Troubleshooting
Traefik logs can be cryptic. AI can translate them into actionable fixes.
Scenario: Certificate acquisition fails. You paste the logs into an AI agent:
time="2025-12-16T10:05:30Z" level=error msg="Unable to obtain ACME certificate for domains "home.example.com""
providerName=letsencrypt.acme error="error: one or more domains had a problem:\n
[home.example.com] acme: error: 403 :: urn:ietf:params:acme:error:unauthorized ::
No TXT record found at _acme-challenge.home.example.com" AI analysis:
Issue: Let's Encrypt DNS-01 challenge failed
Root Cause: Cloudflare DNS TXT record not created
Likely Reasons:
1. CF_API_KEY environment variable incorrect or missing
2. API token lacks "Zone DNS Edit" permission
3. DNS propagation delay (rare, usually <5 seconds)
Recommended Fix:
1. Verify environment variable is set:
docker exec traefik env | grep CF_API_KEY
2. If missing, add to docker-compose.yml:
environment:
- CF_API_KEY=${CF_API_KEY}
3. Recreate API token with correct permissions:
- Cloudflare → API Tokens → Create Token
- Template: "Edit zone DNS"
- Scope: Your specific domain
4. Restart Traefik:
docker-compose restart traefik
5. Monitor logs for success:
docker-compose logs -f traefik | grep -i acme
Expected resolution time: 2-3 minutes This kind of contextual troubleshooting is where AI really shines - it connects the error message to the root cause and provides a step-by-step fix.
3. Security Audit and Recommendations
AI can scan your Traefik configuration and suggest security improvements.
AI audit prompt: “Review my Traefik setup and suggest security improvements”
AI analysis:
Security Audit Results for Traefik Homelab:
CRITICAL Issues:
1. Plex service exposed without rate limiting
Risk: Vulnerable to brute force attacks
Fix: Add rate-limit middleware
2. Docker socket mounted directly (not via socket proxy)
Risk: Container escape vulnerability
Fix: Deploy docker-socket-proxy container
HIGH Priority:
3. No geoblocking on internet-facing services
Risk: Global attack surface
Fix: Consider CrowdSec plugin or geoblock middleware
4. Basic auth using MD5 hashing
Risk: Weak password hashing
Fix: Use bcrypt (htpasswd -B flag)
MEDIUM Priority:
5. No HSTS header on several services
Risk: Downgrade attacks possible
Fix: Apply security-headers middleware globally
6. Certificate backup strategy not implemented
Risk: Certificate loss on disk failure
Fix: Backup acme.json daily to remote location
Recommended Actions:
1. Deploy socket proxy (5 min)
2. Update password hashing (2 min)
3. Add rate limiting to exposed services (10 min)
4. Implement certificate backup (15 min) Time saved: Hours of manual security research and configuration review.
4. AI-Generated Documentation
AI can auto-generate runbooks, architecture diagrams, and disaster recovery procedures from your actual configuration.
Example: “Generate a disaster recovery runbook for my Traefik setup”
AI output:
# Traefik Disaster Recovery Runbook
## Service Architecture
- Entry Points: HTTP (80), HTTPS (443)
- Certificate Resolver: Let's Encrypt (Cloudflare DNS-01)
- Active Services: 8 (Jellyfin, Plex, Home Assistant, Portainer, etc.)
- Certificate Count: 1 wildcard (*.home.example.com)
## Backup Critical Files
Priority 1 (daily backup required):
- ~/homelab/traefik/acme.json (SSL certificates)
- ~/homelab/traefik/.env (sensitive credentials)
Priority 2 (version controlled):
- ~/homelab/traefik/docker-compose.yml
- ~/homelab/traefik/traefik.yml
- ~/homelab/traefik/dynamic.yml
## Recovery Procedure (Total Time: 10-15 minutes)
Step 1: Restore Configuration Files
- Copy backed-up files to ~/homelab/traefik/
- Verify acme.json permissions: chmod 600 acme.json
Step 2: Recreate Docker Network
- docker network create traefik
Step 3: Start Traefik
- cd ~/homelab/traefik && docker-compose up -d
Step 4: Verify Services
- Check dashboard: https://traefik.home.example.com
- Verify certificates: curl -v https://jellyfin.home.example.com 2>&1 | grep 'subject:'
- Test service access: Visit each service URL
Step 5: Restart Dependent Services
- cd ~/homelab/jellyfin && docker-compose restart
- cd ~/homelab/plex && docker-compose restart
(Repeat for all services)
## Validation Checklist
- [ ] Traefik dashboard accessible
- [ ] All routers show "success" status
- [ ] Certificates valid (check expiration dates)
- [ ] All services responding on HTTPS
- [ ] Authentication working on protected services
- [ ] No errors in logs: docker-compose logs traefik This documentation is generated from your actual configuration - it’s always accurate and up-to-date.
Troubleshooting Common Issues
You’ve got this far, but things don’t always work perfectly the first time. Here are the most common issues and their fixes.
Issue 1: “Unable to obtain ACME certificate”
Symptoms: HTTPS not working, certificate errors in logs
Diagnostic steps:
# Step 1: Check if domain resolves correctly
nslookup home.example.com
# Should return your public IP address
# Step 2: Check Traefik logs for specific error
docker-compose logs traefik | grep -i acme | grep -i error
# Step 3: Verify Cloudflare credentials
docker exec traefik env | grep CF_API
# Step 4: Test DNS challenge manually (if using DNS-01)
# Cloudflare should show TXT record _acme-challenge.home.example.com when Traefik requests cert Common causes and fixes:
| Cause | Fix |
|---|---|
| Port 80 blocked | Open firewall, check ISP restrictions, or use DNS-01 |
| Domain doesn’t resolve | Update DNS A record at registrar |
| Wrong API credentials | Verify CF_API_KEY in .env file |
| Rate limit hit | Use staging server: caServer: https://acme-staging-v02.api.letsencrypt.org/directory |
| Multiple Traefik instances | Only one Traefik should handle ACME per domain |
Pro tip: Always test with Let’s Encrypt staging server first. Production has strict rate limits (50 certs per domain per week).
Issue 2: Service Not Discovered by Traefik
Symptoms: Service deployed but not accessible, no router in dashboard
Diagnostic steps:
# Step 1: Verify service is running
docker ps | grep SERVICE_NAME
# Step 2: Check if service is on traefik network
docker inspect SERVICE_NAME | grep -A 10 Networks
# Step 3: Verify labels are correct
docker inspect SERVICE_NAME | grep -A 50 Labels
# Step 4: Check Traefik provider status
curl -s http://localhost:8080/api/http/routers | jq Common causes and fixes:
| Cause | Fix |
|---|---|
exposedByDefault: false but no traefik.enable=true | Add label: traefik.enable=true |
| Service not on traefik network | Add networks: [traefik] to docker-compose.yml |
| Typo in label name | Double-check spelling: traefik.enable not traefik.enbale |
| Port not specified | Add label: traefik.http.services.NAME.loadbalancer.server.port=8080 |
| Traefik not watching Docker | Check providers.docker.watch: true in traefik.yml |
Quick fix: Restart both services:
docker-compose restart traefik
docker-compose restart SERVICE_NAME Issue 3: Authentication Not Working
Symptoms: BasicAuth bypass or credentials rejected
Diagnostic steps:
# Verify password hash format
docker inspect SERVICE_NAME | grep basicauth
# Test manually with curl
curl -v -u admin:password https://service.home.example.com Common causes and fixes:
| Cause | Fix |
|---|---|
| Single $ instead of $$ | Escape dollar signs: $$apr1$$... |
| Wrong htpasswd format | Use htpasswd -B (bcrypt) not -m (MD5) |
| Middleware not applied to router | Add label: traefik.http.routers.NAME.middlewares=auth |
| Wrong username/password | Regenerate hash: htpasswd -nb admin newpassword |
Issue 4: High Latency / Slow Response
Symptoms: Services responding slowly through Traefik
Diagnostic steps:
# Check response time
time curl -I https://service.home.example.com
# Check Traefik resource usage
docker stats traefik
# Review middleware chain
curl -s http://localhost:8080/api/http/routers/SERVICE_NAME | jq '.middlewares' Optimization checklist:
- ✓ Enable connection pooling (default in Traefik v3)
- ✓ Add health checks to services
- ✓ Remove unnecessary middlewares
- ✓ Check upstream service performance (not Traefik’s fault)
- ✓ Monitor network bandwidth saturation
Pro tip: Traefik adds ~5-15ms latency. If you’re seeing 500ms+, the bottleneck is upstream.
Issue 5: WebSocket Connections Failing
Symptoms: Chat apps, real-time dashboards disconnecting
Solution: Traefik handles WebSockets automatically, but verify these settings:
labels:
- "traefik.http.services.myapp.loadbalancer.server.scheme=ws" # or wss for secure Also check that middleware isn’t blocking upgrade headers.
Issue 6: Certificate Renewal Failure
Symptoms: “Error renewing certificate” in logs 30 days before expiration
Diagnostic steps:
# Check acme.json permissions
ls -l ~/homelab/traefik/acme.json
# Should be -rw------- (600)
# Verify disk space
df -h
# Check DNS provider connectivity
docker exec traefik curl -v https://api.cloudflare.com/client/v4/user/tokens/verify -H "Authorization: Bearer $CF_API_KEY" Fixes:
- Permissions:
chmod 600 acme.json && docker-compose restart traefik - Disk space: Clear old logs, expand storage
- DNS credentials: Verify API token still valid
Pro tip: Set up monitoring alerts for certificate expiration. Traefik exports Prometheus metrics for this.
Security Best Practices
You’ve got Traefik running. Now let’s lock it down.
1. Use Docker Socket Proxy (Critical)
The problem: Mounting /var/run/docker.sock gives Traefik full access to your Docker daemon. A container escape could compromise your entire host.
The solution: Docker Socket Proxy - a tiny container that exposes only the Docker API endpoints Traefik needs.
# ~/homelab/traefik/docker-compose.yml (updated)
version: '3.8'
services:
# Add socket proxy
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: docker-socket-proxy
restart: unless-stopped
security_opt:
- no-new-privileges:true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
# Only allow read-only access to containers
CONTAINERS: 1
SERVICES: 1
TASKS: 1
# Deny dangerous operations
POST: 0
networks:
- traefik
traefik:
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
depends_on:
- docker-socket-proxy
security_opt:
- no-new-privileges:true
networks:
- traefik
ports:
- "80:80"
- "443:443"
environment:
# Connect to socket proxy instead of direct socket
- DOCKER_HOST=tcp://docker-socket-proxy:2375
- CF_API_EMAIL=${CF_API_EMAIL}
- CF_API_KEY=${CF_API_KEY}
volumes:
- /etc/localtime:/etc/localtime:ro
# NO MORE DOCKER SOCKET MOUNT HERE
- ./traefik.yml:/traefik.yml:ro
- ./dynamic.yml:/dynamic.yml:ro
- ./acme.json:/acme.json
labels:
# ... (same as before)
networks:
traefik:
name: traefik
driver: bridge Update traefik.yml:
providers:
docker:
endpoint: tcp://docker-socket-proxy:2375 # Changed from unix socket
# ... rest of config 2. Enforce Strong Password Hashing
Use bcrypt instead of MD5:
# Generate bcrypt hash (much stronger than MD5)
htpasswd -nbB admin your_secure_password
# Output: admin:$2y$05$encrypted_hash_here
# In docker-compose.yml, double the dollar signs: $$2y$$05$$encrypted_hash_here 3. Add Security Headers to All Services
Update your dynamic.yml:
http:
middlewares:
security-headers:
headers:
sslRedirect: true
sslProxyHeaders:
X-Forwarded-Proto: https
customResponseHeaders:
X-Frame-Options: "SAMEORIGIN"
X-Content-Type-Options: "nosniff"
X-XSS-Protection: "1; mode=block"
Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
Referrer-Policy: "strict-origin-when-cross-origin"
Permissions-Policy: "geolocation=(),microphone=(),camera=()" Then apply to services:
labels:
- "traefik.http.routers.myapp.middlewares=security-headers" 4. Implement Rate Limiting
Protect against brute force and DDoS:
http:
middlewares:
aggressive-rate-limit:
rateLimit:
average: 50 # 50 requests
burst: 25 # max 25 concurrent
period: 60s # per minute Apply to public-facing services:
labels:
- "traefik.http.routers.public-service.middlewares=aggressive-rate-limit,security-headers" 5. Backup acme.json Daily
Your certificates are irreplaceable (well, renewable, but still):
#!/bin/bash
# ~/scripts/backup-traefik.sh
BACKUP_DIR="/mnt/backup/traefik"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup certificates
cp ~/homelab/traefik/acme.json $BACKUP_DIR/acme.json.$TIMESTAMP
# Backup configuration
tar -czf $BACKUP_DIR/traefik-config.$TIMESTAMP.tar.gz ~/homelab/traefik/*.yml ~/homelab/traefik/docker-compose.yml
# Keep only last 30 days
find $BACKUP_DIR -name "acme.json.*" -mtime +30 -delete
find $BACKUP_DIR -name "traefik-config.*" -mtime +30 -delete
echo "Traefik backup completed: $TIMESTAMP" Add to cron:
crontab -e
# Add: 0 2 * * * /home/user/scripts/backup-traefik.sh 6. Monitor Certificate Expiration
Set up Prometheus alerts or simple scripts:
#!/bin/bash
# ~/scripts/check-cert-expiration.sh
DOMAIN="home.example.com"
THRESHOLD_DAYS=7
# Get certificate expiration date
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
# Convert to seconds
EXPIRY_SEC=$(date -d "$EXPIRY" +%s)
NOW_SEC=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_SEC - $NOW_SEC) / 86400 ))
if [ $DAYS_LEFT -lt $THRESHOLD_DAYS ]; then
echo "WARNING: Certificate expires in $DAYS_LEFT days!"
# Send alert (email, Discord webhook, etc.)
fi AI opportunity: An AI agent could monitor these metrics continuously, predict renewal failures based on historical patterns, and auto-fix common issues before certificates expire.
Next Steps: Level Up Your Homelab
You’ve deployed Traefik, secured your services, and automated SSL certificates. Here’s what’s next:
Immediate Actions (Next 1-2 Hours)
Deploy 2-3 More Services: Get comfortable with the label pattern
- Try Plex, Home Assistant, or NextCloud
- Experiment with different middleware combinations
- Test path-based routing vs subdomain routing
Set Up Monitoring: Connect Traefik to Prometheus + Grafana
- Import official Grafana dashboard (ID: 17346)
- Set up alerts for certificate expiration
- Monitor request latency and error rates
Implement Centralized Auth: Add Authentik or Authelia
- Single sign-on for all services
- Two-factor authentication
- Fine-grained access control
Advanced Topics (Next 1-2 Weeks)
Add CrowdSec Integration: Real-time threat detection
- Block malicious IPs automatically
- Share threat intelligence with community
- Protect against common attack patterns
Implement GitOps Workflow: Version control everything
- Store all configs in Git repository
- Use CI/CD for deployments
- Automated testing before deployment
Scale to Multiple Nodes: Traefik + Docker Swarm or Kubernetes
- High availability reverse proxy
- Load balancing across multiple servers
- Automatic failover
AI-Assisted Future (Coming Soon)
The future of homelabbing is AI-driven automation:
Predictive Maintenance: AI monitors your logs and metrics, predicting issues before they happen
- “Certificate renewal will fail in 3 days due to DNS provider rate limiting”
- “Memory usage trending up 2% per day, will hit limit in 45 days”
Self-Healing Infrastructure: AI detects and fixes common issues automatically
- Service health check fails → AI restarts container → Monitors recovery
- Certificate expiration approaching → AI renews early → Validates new cert
Intelligent Optimization: AI tunes configuration based on usage patterns
- “Your rate limiting is too aggressive, blocking 10% of legitimate traffic”
- “Middleware ordering inefficient, reorder for 25% faster response times”
Security Co-Pilot: AI continuously audits and hardens your setup
- “New CVE affects Jellyfin 10.8.0, update available, apply patch?”
- “Detected brute force attempts from IP X.X.X.X, add to blocklist?”
Want to explore these AI capabilities? Join the homelabbing.io community - we’re building AI tools specifically for homelabbers.
Key Takeaways
You’ve learned a lot. Here’s what to remember:
Core Principles:
- Infrastructure as Code: Everything in version control, reproducible deployments
- Zero-Touch Automation: Automatic SSL, service discovery, minimal manual work
- Security First: Authentication, rate limiting, security headers built-in
- Cloud-Native: Designed for containers, scales from single server to clusters
Why Traefik Wins:
- More intelligent than manual Nginx (no config rewriting)
- More automated than Nginx Proxy Manager (scales better, IaC approach)
- More powerful than Caddy (Kubernetes-ready, enterprise backing)
- Perfect balance of simplicity and capability
Your Journey: Start small (Traefik + 2-3 services) → Expand gradually → Master security → Implement monitoring → Embrace AI automation → Scale with confidence
What You’ve Accomplished:
- ✓ Deployed Traefik with automatic HTTPS
- ✓ Configured Let’s Encrypt with wildcard certificates
- ✓ Enabled Docker service auto-discovery
- ✓ Implemented security best practices
- ✓ Learned troubleshooting techniques
- ✓ Explored AI-assisted management opportunities
The homelabbing.io Difference: We don’t just teach you tools - we show you how AI can multiply your productivity. Every manual task is an automation opportunity. Every repetitive configuration is a chance for AI-generated templates. Every log file is training data for predictive analytics.
Welcome to the future of homelabbing. You’ve got this.
Resources
Official Documentation:
Community Resources:
Related Guides on homelabbing.io:
- Docker Security Best Practices
- Centralized Authentication with Authentik
- Monitoring Homelab Services with Prometheus
- Building an AI-Powered Homelab
Tools Mentioned:
Questions? Issues? Insights? Drop a comment below or join the homelabbing.io community. We’re all learning together.
Found this helpful? Share it with fellow homelabbers who are still manually managing Nginx configs. They’ll thank you.
Now go build something awesome. Your homelab is waiting.