This guide shows how to deploy a modern reverse proxy using Traefik v3, Docker, and Docker Compose on Ubuntu 22.04 or 24.04. You will get automatic Let’s Encrypt SSL certificates, HTTP to HTTPS redirection, and a clean way to route multiple apps on the same server under different domains or subdomains. The steps are simple, repeatable, and safe for production.
Prerequisites
Before starting, ensure you have: (1) An Ubuntu 22.04 or 24.04 server with a public IP, (2) A domain or subdomain you can edit DNS for (e.g., example.com), and (3) Access to open TCP ports 80 and 443 on the server’s firewall and network provider. You should also have a non-root user with sudo access.
Step 1 — Install Docker Engine and Compose Plugin
Install the official Docker packages. This gives you the latest stable Docker Engine, Buildx, and the Compose plugin.
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release; echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
Step 2 — DNS and Firewall
Create DNS A records for your services. For example, point app.example.com and traefik.example.com to your server’s public IP. Then open the firewall for web traffic.
sudo ufw allow 80,443/tcp
sudo ufw reload
Step 3 — Prepare a Docker Network and Traefik Files
Create a dedicated Docker network for the proxy and a directory to hold Traefik files, including the ACME storage for certificates.
docker network create proxy
mkdir -p ~/traefik
cd ~/traefik
touch acme.json
chmod 600 acme.json
Step 4 — Create docker-compose.yml for Traefik v3
The compose file below configures Traefik v3 with automatic HTTPS via the HTTP-01 challenge (ports 80/443). It also binds the dashboard to localhost only for safety.
version: "3.9"
networks:
proxy:
external: true
services:
traefik:
image: traefik:v3.1
container_name: traefik
command:
- --api.dashboard=true
- --api.insecure=false
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- [email protected]
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --log.level=INFO
ports:
- "80:80"
- "443:443"
- "127.0.0.1:8080:8080" # Dashboard on localhost
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./acme.json:/letsencrypt/acme.json
networks:
- proxy
restart: unless-stopped
Replace [email protected] with an email you control. Traefik uses it to register with Let’s Encrypt. The dashboard is available on http://localhost:8080 and can be reached via SSH tunneling when needed.
Step 5 — Start Traefik
Launch the reverse proxy and confirm it is running.
docker compose up -d
docker ps
You should see the traefik container healthy and listening on ports 80 and 443.
Step 6 — Add a Test App Behind Traefik
Deploy a simple test service (whoami) to verify automatic HTTPS, routing, and headers. Replace app.example.com with your real hostname that points to the server.
docker run -d --name whoami --network proxy --restart unless-stopped \
-l "traefik.enable=true" \
-l "traefik.http.routers.who.rule=Host(`app.example.com`)" \
-l "traefik.http.routers.who.entrypoints=websecure" \
-l "traefik.http.routers.who.tls.certresolver=le" \
-l "traefik.http.middlewares.secHeaders.headers.stsSeconds=31536000" \
-l "traefik.http.middlewares.secHeaders.headers.stsIncludeSubdomains=true" \
-l "traefik.http.middlewares.secHeaders.headers.stsPreload=true" \
-l "traefik.http.middlewares.secHeaders.headers.frameDeny=true" \
-l "traefik.http.middlewares.ratelimit.rateLimit.average=100" \
-l "traefik.http.middlewares.ratelimit.rateLimit.burst=50" \
-l "traefik.http.routers.who.middlewares=secHeaders@docker,ratelimit@docker" \
traefik/whoami:v1.10
Open https://app.example.com in a browser. The first request may take a few seconds while Traefik obtains a certificate. You should see a basic whoami page over HTTPS with a valid lock icon.
Optional: Use DNS-01 Challenge (Wildcard Certificates)
If your ISP or host blocks port 80, or you want wildcard certificates like *.example.com, switch to the DNS-01 challenge. The example below uses Cloudflare. Create a token in Cloudflare with DNS edit permissions for the zone and store it in an .env file.
cd ~/traefik
printf "CF_DNS_API_TOKEN=YOUR_CLOUDFLARE_DNS_TOKEN\n" > .env
Edit docker-compose.yml and replace the ACME lines for HTTP challenge with DNS challenge:
- --certificatesresolvers.le.acme.dnschallenge=true
- --certificatesresolvers.le.acme.dnschallenge.provider=cloudflare
Add the environment variable to the traefik service:
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
Then reload Traefik:
docker compose up -d
For a wildcard, use routers or certificate domains that match *.example.com. With DNS challenge, Traefik can issue certificates without exposing port 80.
Secure the Dashboard (Optional but Recommended)
By default we bound the dashboard to localhost. To access it securely from your workstation, use an SSH tunnel: ssh -L 8080:localhost:8080 user@your_server and then open http://localhost:8080. If you must expose it on a domain, add Basic Auth and IP allowlisting via labels and serve it over HTTPS. For most setups, keeping it local is safer.
Troubleshooting
If certificates do not issue, confirm that your DNS A/AAAA records point to the server and that ports 80 and 443 are reachable from the internet. Check logs with docker logs -f traefik. Rate limits from Let’s Encrypt can apply if you redeploy too often; use a single domain during testing and switch to the production domain when stable.
Maintenance
Traefik renews certificates automatically before expiry; no cron is required. Keep Docker images current by pulling updates periodically: docker compose pull and docker compose up -d. Back up the acme.json file; it contains your issued certificates and keys.
What You Achieved
You now have a modern, production-ready reverse proxy with Traefik v3, automatic HTTPS via Let’s Encrypt, and a clean Docker-based workflow. Adding new apps is as simple as attaching them to the proxy network and setting a few labels. This pattern scales well, stays secure, and keeps your server easy to manage.
Comments
Post a Comment