Docker Compose Traefik v3 reverse proxy supporting both local development and production environments with enhanced security via Docker Socket Proxy and Authelia SSO.
Uses *.localhost for local domains with automatic HTTP routing, or custom domains in production with HTTPS via Let's Encrypt.
Follow these steps in order when setting up for the first time:
docker network create traefikRun the Authelia setup script to create directories, generate cryptographic secrets, and create your initial admin user:
./setup-authelia.shThis script only needs to run once and works for both local and production environments. It will:
- Create
authelia/config/,authelia/secrets/, andauthelia/data/directories - Generate secure random secrets (JWT, session, encryption, storage keys)
- Prompt you to create an admin user with password
docker compose up -dThe Traefik dashboard is now accessible at: http://traefik.localhost
When prompted, log in with the admin credentials you created in step 2.
If deploying to production, run:
./setup-production.shThen edit .env to set your domain, Cloudflare API token, and SMTP credentials (see Production Deployment section below).
This setup uses file-based configuration with separate config files for local development and production environments.
This setup includes multiple layers of security:
A Docker Socket Proxy (wollomatic/socket-proxy) sits between Traefik and the Docker socket, using regex-based access control to limit Traefik's access to only the Docker API endpoints it needs (containers, networks, and events), with all write operations disabled.
Key features:
- Built in Go with zero dependencies (minimal attack surface)
- Regex-based permission rules for fine-grained API access control
- Hostname-based allowlisting (only the
traefikcontainer can connect) - Read-only filesystem and dropped capabilities
- Socket watchdog for automatic recovery from Docker daemon issues
Authelia provides centralized authentication and access control for the Traefik dashboard and any services routed through Traefik.
Key features:
- Single Sign-On (SSO) for all protected services
- Argon2id password hashing
- Two-Factor Authentication (2FA) support via TOTP
- Session-based authentication
- File-based user database (suitable for small teams)
- Access control rules per domain/path
Configuration files:
authelia/config/configuration-local.yml- File-based notifications for local developmentauthelia/config/configuration-prod.yml- SMTP email notifications for productionauthelia/data/users_database.yml- User credentials (created bysetup-authelia.sh)
Both Traefik and socket-proxy run as non-root users, preventing root-owned files in bind mounts:
- Traefik: Runs as the host user (UID/GID from .env, typically 1000:1000)
- Socket Proxy: Runs as user 65534 (nobody) with Docker group GID for socket access
This ensures all files in letsencrypt/ and logs/ are owned by your user account, eliminating the need for sudo.
The default configuration uses config/traefik-local.yaml:
- HTTP only on port 80
- Traefik dashboard accessible at http://traefik.localhost
- Debug logging enabled
- No SSL/HTTPS
No changes needed - just run docker compose up -d
Prerequisites: You must have already run ./setup-authelia.sh (see Initial Setup above) before switching to production.
Run the production setup script:
./setup-production.shThis automatically:
- Switches from
traefik-local.yamltotraefik-prod.yaml - Switches from
authelia/configuration-local.ymltoauthelia/configuration-prod.yml - Enables port 443 for HTTPS
- Enables
letsencryptandlogsvolume mounts - Enables Cloudflare DNS environment variables
- Enables Authelia SMTP environment variables
- Auto-detects your UID, GID, and Docker group ID
- Creates
.envfrom.env.examplewith detected IDs (if not exists) - Substitutes template placeholders in Authelia config with values from
.env - Creates
letsencrypt/directory with correct ownership - Creates
acme.jsonwith 600 permissions and correct ownership - Creates
logs/directory with correct ownership - Verifies
traefiknetwork exists - Creates a timestamped backup of
compose.yaml
After running the script:
-
Edit
.envand set your actual values:# Traefik configuration DOMAIN=example.com CF_DNS_API_TOKEN=your_cloudflare_api_token_here # Authelia SMTP configuration (for email notifications) SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_USERNAME=your_smtp_username SMTP_PASSWORD=your_smtp_password SMTP_FROM=authelia@example.com ADMIN_EMAIL=admin@example.com
-
Re-run setup script to substitute placeholders with your actual values:
./setup-production.sh
-
Verify configuration before deploying:
./verify-config.sh
-
Deploy services:
docker compose down docker compose up -d
To revert to local development configuration:
./setup-local.sh
docker compose up -dUse the verification script to check for common configuration issues before deployment:
./verify-config.shThe script checks for:
- Missing or unset environment variables in
.env - Unsubstituted template placeholders (e.g.,
{{DOMAIN}},{{SMTP_HOST}}) - Docker group ID mismatches that cause socket-proxy permission errors
- Hardcoded example.com domains in production configs
- Active configuration files in
compose.yaml - SMTP configuration for Authelia
- Let's Encrypt
acme.jsonfile permissions - Docker network existence
- Authelia initialization status
Exit codes:
0- All checks passed or only warnings found1- Errors found that must be fixed before deployment
Run this script both before deploying to production and after making any configuration changes to catch issues early.
If you prefer to configure manually, see the detailed steps in CLAUDE.md.
config/traefik-local.yaml- Local development (HTTP only)config/traefik-prod.yaml- Production (HTTPS with Cloudflare DNS challenge)config/conf/- Directory for dynamic configuration files (routers, middlewares, services)
authelia/config/configuration-local.yml- Local development (file-based notifications)authelia/config/configuration-prod.yml- Production (SMTP email notifications)authelia/config/users_database.yml- User credentials (gitignored, created bysetup-authelia.sh)authelia/secrets/- Cryptographic secrets (gitignored, auto-generated)authelia/data/- Runtime data (gitignored)
To add additional users or change passwords, edit authelia/data/users_database.yml:
# Generate a password hash
docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'your-password-here'
# Edit the users database
nano authelia/data/users_database.ymlAdd the new user following the existing format, then restart Authelia:
docker compose restart autheliaTo configure a Docker project to use this Traefik proxy, include the external network in your project's docker-compose.yml:
networks:
traefik:
external: true
services:
nginx:
image: shaneturner/nginx:alpine
init: true
restart: unless-stopped
labels:
# Enable Traefik for this service
- "traefik.enable=true"
# Define the domain/URL (use ${DOMAIN:-localhost} for env-based domains)
- "traefik.http.routers.laravel.rule=Host(`laravel.${DOMAIN:-localhost}`)"
# Specify the entrypoint (web for HTTP, websecure for HTTPS)
- "traefik.http.routers.laravel.entrypoints=web"
# Define which network Traefik should use to find this service
- "traefik.docker.network=traefik"
# Specify the port that Traefik should proxy to
- "traefik.http.services.laravel.loadbalancer.server.port=80"
volumes:
- ./src:/var/www/html
networks:
- traefik # External network for Traefik
- default # Internal network for service communication
depends_on:
- postgres
- php
postgres:
image: postgres:17
init: true
restart: unless-stopped
ports:
- "5432"
environment:
POSTGRES_DB: laravel
POSTGRES_USER: laravel
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD", "pg_isready", "-q", "-d", "laravel", "-U", "laravel"]
retries: 3
timeout: 5s
volumes:
- data:/var/lib/postgresql/data
networks:
- default # Only needs internal network
php:
image: shaneturner/php:8.3
init: true
restart: unless-stopped
depends_on:
- postgres
volumes:
- ./src:/var/www/html
networks:
- default # Only needs internal network
volumes:
data:To require authentication for a service, add the Authelia middleware to its router labels:
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.${DOMAIN:-localhost}`)"
- "traefik.http.routers.myapp.entrypoints=web"
- "traefik.docker.network=traefik"
# Add Authelia middleware for authentication
- "traefik.http.routers.myapp.middlewares=authelia@file"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
networks:
- traefikThe authelia@file middleware is already configured in config/conf/authelia-middleware.yaml. Users will be redirected to the Authelia login page before accessing the service.
- First-time setup: You must run
./setup-authelia.shbefore starting Traefik for the first time - Setup script order: 1) Create network, 2) Run
setup-authelia.sh, 3) Start services, 4) Optionally runsetup-production.sh - The nginx service uses both the
traefiknetwork (for proxy access) and thedefaultnetwork (for internal service communication) - Services that don't need external access (like
postgresandphp) only use thedefaultnetwork - For HTTPS/SSL setups, change the entrypoint from
webtowebsecurein your service labels - The current setup uses Traefik v3 with file-based configuration
- All containers run as non-root users to prevent permission issues with bind mounts