Welcome to Part 2! In Part 1, we covered what Traefik is and why it's awesome. Now we're going to build it.
By the end of this guide, you'll have Traefik running with automatic HTTPS, and you'll be able to access the Traefik dashboard at traefik.yourdomain.com.
Overview: What We're Building Today
Here's what we'll accomplish in this tutorial:
- ✅ Set up a VPS with Ubuntu
- ✅ Install Docker and Docker Compose
- ✅ Configure DNS for your domain
- ✅ Create the Docker network for Traefik
- ✅ Write the Traefik configuration
- ✅ Deploy Traefik container
- ✅ Get your first automatic SSL certificate
- ✅ Access the Traefik dashboard
Estimated time: 1-2 hours
Step 1: VPS Setup
Choosing a VPS Provider
You'll need a server with a public IP address. Here are some reliable options:
Budget-Friendly ($5-10/month):
- DigitalOcean - Great documentation, simple interface
- Linode (now Akamai) - Reliable, good performance
- Vultr - Fast deployment, many locations
- Hetzner - Best price/performance ratio (Europe-based)
What specs do you need?
- Minimum: 1GB RAM, 1 vCPU, 25GB storage
- Recommended: 2GB RAM, 2 vCPU, 50GB storage
- OS: Ubuntu 22.04 LTS (what we'll use in this guide)
Initial Server Access
Once your VPS is created, you'll get:
- An IP address (e.g.,
203.0.113.45) - SSH credentials
Connect to your server:
ssh root@203.0.113.45
If prompted about host authenticity, type yes.
Update the System
First, let's make sure everything is up to date:
# Update package lists
apt update
# Upgrade all packages
apt upgrade -y
# Reboot if kernel was updated (optional but recommended)
reboot
Why this matters: Security updates and bug fixes. An outdated system is vulnerable to known exploits.
After reboot, reconnect:
ssh root@203.0.113.45
Create a Non-Root User (Recommended)
Running everything as root is risky. Let's create a regular user:
# Create a user (replace 'yourname' with your preferred username)
adduser yourname
# Add to sudo group for admin privileges
usermod -aG sudo yourname
# Add to docker group (we'll create this later)
usermod -aG docker yourname
You can now use this user for everything:
# From your local machine
ssh yourname@203.0.113.45
For this tutorial, I'll use the root user for simplicity, but in production, use your regular user with sudo.
Step 2: Install Docker
Docker is the foundation of our setup. Traefik will run in a Docker container and manage other Docker containers.
Why Docker?
- Isolation: Each app runs in its own environment
- Consistency: Works the same on any server
- Easy deployment: Start/stop apps with simple commands
- Resource efficiency: Lighter than virtual machines
Installation Method 1: Official Docker Script (Recommended)
The easiest way to install Docker:
# Download and run Docker's installation script
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# Start Docker service
systemctl start docker
systemctl enable docker
# Verify installation
docker --version
You should see something like: Docker version 24.0.7, build afdd53b
Installation Method 2: Manual Installation (Ubuntu)
If you prefer manual installation:
# Install prerequisites
apt install -y apt-transport-https ca-certificates curl software-properties-common
# Add Docker's 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 -y docker-ce docker-ce-cli containerd.io
# Start Docker
systemctl start docker
systemctl enable docker
Install Docker Compose
Docker Compose lets us define multi-container applications in YAML files:
# Install Docker Compose plugin
apt install -y docker-compose-plugin
# Or for standalone docker-compose (older method)
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# Verify
docker compose version
You should see: Docker Compose version v2.x.x
Test Docker Installation
Let's verify everything works:
# Run a test container
docker run hello-world
If you see "Hello from Docker!" message, you're good to go!
Clean up the test container:
docker rm $(docker ps -a -q --filter ancestor=hello-world)
docker rmi hello-world
Step 3: Configure Your Domain and DNS
Traefik needs to know which domains to handle, and your domains need to point to your server.
Get Your Domain
If you don't have a domain yet, register one at:
- Namecheap - Easy to use, good prices
- Cloudflare - Great if you'll use their services
- Porkbun - Developer-friendly, good prices
- Google Domains → Now Squarespace
For this guide, I'll use lans.cloud as an example. Replace it with your actual domain.
Understanding DNS Records
Before we configure DNS, let's understand what we're doing:
A Record: Maps a domain to an IPv4 address
example.com → 203.0.113.45
CNAME Record: Maps a domain to another domain
www.example.com → example.com
Wildcard Record: Matches any subdomain
*.example.com → 203.0.113.45
DNS Configuration Option 1: Wildcard DNS (Recommended)
A wildcard record means ALL subdomains point to your server. This is perfect for Traefik because you can add new services without updating DNS.
In your domain registrar's DNS settings:
| Type | Host | Value | TTL |
|---|---|---|---|
| A | @ | 203.0.113.45 | 300 |
| A | * | 203.0.113.45 | 300 |
What this does:
@= your root domain (example.com)*= ALL subdomains (anything.example.com)- Both point to your server's IP
- TTL 300 = 5 minutes (DNS changes propagate faster during setup)
DNS Configuration Option 2: Individual Records
If you prefer explicit control:
| Type | Host | Value | TTL |
|---|---|---|---|
| A | @ | 203.0.113.45 | 300 |
| A | traefik | 203.0.113.45 | 300 |
| A | blog | 203.0.113.45 | 300 |
| A | api | 203.0.113.45 | 300 |
Downside: You must add a DNS record for every new service.
I recommend wildcard for flexibility.
Verify DNS Propagation
DNS changes can take 5 minutes to 48 hours to propagate. Check if it's working:
# On your local machine or server
dig traefik.lans.cloud
# Or use nslookup
nslookup traefik.lans.cloud
# Quick online check
# Visit: https://dnschecker.org
You should see your server's IP address in the response.
If it doesn't work yet:
- Wait 5-10 minutes
- Clear your DNS cache:
sudo systemd-resolve --flush-caches(Linux) - Try
dig @8.8.8.8 traefik.lans.cloud(forces Google's DNS)
Step 4: Firewall Configuration
Before deploying Traefik, let's make sure the right ports are open.
Check Current Firewall
# If using UFW (Ubuntu Firewall)
ufw status
# If using iptables
iptables -L
Configure UFW (Ubuntu/Debian)
# Enable UFW
ufw enable
# Allow SSH (IMPORTANT: do this first or you'll lock yourself out!)
ufw allow 22/tcp
# Allow HTTP and HTTPS (what Traefik uses)
ufw allow 80/tcp
ufw allow 443/tcp
# Check status
ufw status verbose
You should see:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
Configure iptables (Alternative)
# Allow SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Save rules
iptables-save > /etc/iptables/rules.v4
Important: Most VPS providers also have firewall settings in their web dashboard. Check there too!
Step 5: Create the Traefik Directory Structure
Let's organize our Traefik files:
# Create main directory
mkdir -p /home/yourname/traefik
cd /home/yourname/traefik
# Create subdirectories
mkdir -p letsencrypt
mkdir -p logs
# Set permissions
chmod 600 letsencrypt
Directory structure:
/home/yourname/traefik/
├── docker-compose.yml # Will define Traefik container
├── traefik.yml # Traefik static configuration
├── letsencrypt/ # SSL certificates stored here
│ └── acme.json # Let's Encrypt account data
└── logs/ # Access and error logs
├── access.log
└── traefik.log
Why this structure?
- Keeps everything organized
- Easy to back up
- Certificates persist across container restarts
- Logs accessible for debugging
Step 6: Create Docker Network
Traefik needs a dedicated Docker network to communicate with your applications.
# Create the network
docker network create traefik-network
# Verify it was created
docker network ls
You should see traefik-network in the list.
What's happening here?
This creates a Docker bridge network. Containers on this network can:
- Find each other by container name
- Communicate on any port (internal to the network)
- Be discovered by Traefik
Think of it like a private LAN for your containers.
Step 7: Configure Traefik
Now for the exciting part - configuring Traefik itself. We'll create two files:
- traefik.yml - Static configuration (rarely changes)
- docker-compose.yml - Defines the Traefik container
Create traefik.yml
This file contains Traefik's core configuration:
nano /home/yourname/traefik/traefik.yml
Paste this content:
# Traefik Static Configuration
# Entry points (ports Traefik listens on)
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
# Docker provider (tells Traefik to watch Docker)
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # Only expose containers with traefik.enable=true
network: traefik-network
# Let's Encrypt configuration
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com # CHANGE THIS!
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
# Enable API and Dashboard
api:
dashboard: true
insecure: false # Dashboard will be behind authentication
# Logging
log:
level: INFO
filePath: /logs/traefik.log
accessLog:
filePath: /logs/access.log
Important: Change your-email@example.com to your actual email. Let's Encrypt uses this for:
- Certificate expiration notifications
- Account recovery
- Important security updates
Save and exit: Ctrl+X, then Y, then Enter
Understanding the Configuration
Let's break down what each section does:
Entry Points:
entryPoints:
web:
address: ":80" # HTTP port
websecure:
address: ":443" # HTTPS port
These define which ports Traefik listens on. The web entrypoint auto-redirects to websecure (HTTPS).
HTTP → HTTPS Redirect:
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
This ensures all traffic uses HTTPS. permanent: true sends a 301 redirect (SEO-friendly).
Docker Provider:
providers:
docker:
exposedByDefault: false
network: traefik-network
exposedByDefault: falseis a security feature - containers must explicitly opt-innetworktells Traefik which network to use
Let's Encrypt (ACME):
certificatesResolvers:
letsencrypt:
acme:
email: your@email.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
httpChallengemeans Let's Encrypt will verify domain ownership via HTTPstorageis where certificates are saved
Create docker-compose.yml
This file defines how to run the Traefik container:
nano /home/yourname/traefik/docker-compose.yml
Paste this content:
version: '3.8'
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-network
ports:
- 80:80
- 443:443
environment:
- TZ=America/New_York # Change to your timezone
volumes:
# Traefik config
- ./traefik.yml:/traefik.yml:ro
# Let's Encrypt certificates
- ./letsencrypt:/letsencrypt
# Docker socket (so Traefik can detect containers)
- /var/run/docker.sock:/var/run/docker.sock:ro
# Logs
- ./logs:/logs
labels:
# Enable Traefik for its own dashboard
- "traefik.enable=true"
# Dashboard routing
- "traefik.http.routers.dashboard.rule=Host(`traefik.lans.cloud`)" # CHANGE THIS!
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
# Basic auth for dashboard (username: admin, password: change-this)
# Generate your own with: echo $(htpasswd -nb admin your-password)
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$8EVjn/nj$$GiLUZqcbueTFeD23SuB6x0"
# Apply auth middleware to dashboard
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
networks:
traefik-network:
external: true
Important changes to make:
-
Change the domain: Replace
traefik.lans.cloudwith your domain (e.g.,traefik.yourdomain.com) -
Change the password: The default password is
changethis. Generate a new one:
# Install apache2-utils (contains htpasswd)
apt install -y apache2-utils
# Generate password hash
echo $(htpasswd -nb admin yourpassword)
Copy the output (everything including admin:...) and replace the users line in the labels.
Important about the password hash:
- Note the double
$$in the YAML file - This is NOT a typo - Docker Compose requires escaping the
$character - If you copy from
htpasswdoutput, convert single$to$$
Example:
# htpasswd output:
admin:$apr1$8EVjn/nj$GiLUZqcbueTFeD23SuB6x0
# In docker-compose.yml:
admin:$$apr1$$8EVjn/nj$$GiLUZqcbueTFeD23SuB6x0
Save and exit: Ctrl+X, then Y, then Enter
Create acme.json File
Let's Encrypt stores certificate data in this file:
touch /home/yourname/traefik/letsencrypt/acme.json
chmod 600 /home/yourname/traefik/letsencrypt/acme.json
Why chmod 600?
This sets the file to be readable/writable only by the owner. Let's Encrypt requires this for security - if the file is too permissive, Traefik will refuse to start.
Step 8: Deploy Traefik
Everything is configured. Time to start Traefik!
# Make sure you're in the traefik directory
cd /home/yourname/traefik
# Start Traefik
docker compose up -d
What happens:
- Docker downloads the Traefik image (first time only)
- Creates and starts the container
- Traefik reads its configuration
- Starts listening on ports 80 and 443
- Begins watching for Docker containers
Check if it's running:
docker ps
You should see:
CONTAINER ID IMAGE STATUS PORTS NAMES
abc123def456 traefik:v2.10 Up 10 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp traefik
View logs:
docker logs traefik
Look for errors. A successful start shows:
Configuration loaded from file: /traefik.yml
Traefik version 2.10.x
Step 9: Test and Access the Dashboard
Your Traefik dashboard should now be accessible at https://traefik.yourdomain.com
First-Time Access
-
Open your browser and go to:
https://traefik.yourdomain.com -
You'll see a security warning (normal on first access while certificate is being provisioned)
- Wait 30-60 seconds
- Refresh the page
- Certificate should now be valid
-
Login prompt appears
- Username:
admin - Password: whatever you set in the htpasswd step
- Username:
-
Dashboard loads! You should see:
- Entry points (web, websecure)
- Routers (dashboard route)
- Services (api@internal)
- No other applications yet (we'll add them in Part 3)
Understanding the Dashboard
The dashboard shows you:
HTTP Section:
- Routers: URL routing rules
- Services: Your applications
- Middlewares: Things that modify requests (auth, CORS, etc.)
TCP/UDP Sections: For non-HTTP traffic
Explorer Tab: Detailed view of configuration
Verify SSL Certificate
Check that your certificate is valid:
# On your server or local machine
curl -I https://traefik.yourdomain.com
Look for:
HTTP/2 200
...
HTTP/2 confirms HTTPS is working.
Or use an online tool:
- Visit https://www.ssllabs.com/ssltest/
- Enter your domain
- Should get an A rating
Step 10: Verification Checklist
Let's verify everything is working:
- [ ] Server accessible via SSH
- [ ] Docker installed and running
- [ ] Domain DNS points to your server IP
- [ ] Firewall allows ports 22, 80, 443
- [ ] traefik-network created
- [ ] Traefik container running
- [ ] Dashboard accessible via HTTPS
- [ ] SSL certificate is valid (green padlock)
- [ ] Basic authentication works
If any of these fail, see the Troubleshooting section below.
Troubleshooting Common Issues
Issue 1: Can't Access Dashboard
Symptoms: Browser shows "This site can't be reached" or times out
Causes and solutions:
-
DNS not propagated yet
dig traefik.yourdomain.comIf it doesn't show your server IP, wait or check DNS configuration.
-
Firewall blocking
ufw status # Should show 80 and 443 are allowed -
Traefik not running
docker ps | grep traefik # Should show container running -
Wrong domain in docker-compose.yml
- Check the Host() rule matches your actual domain
Issue 2: SSL Certificate Error
Symptoms: Browser shows "Your connection is not private" or "SEC_ERROR_UNKNOWN_ISSUER"
Causes:
-
Certificate still being provisioned (most common)
- Wait 60 seconds and refresh
- Check logs:
docker logs traefik | grep -i acme
-
Let's Encrypt rate limit hit
- Let's Encrypt allows 5 certificates per week per domain
- Wait a week or use a subdomain you haven't tried yet
-
DNS not pointing to your server
- Let's Encrypt can't verify domain ownership
- Fix DNS and restart:
docker compose restart
-
Email not configured in traefik.yml
- Let's Encrypt requires a valid email
Issue 3: acme.json Permission Error
Symptoms: Logs show "error reading acme.json: open /letsencrypt/acme.json: permission denied"
Solution:
chmod 600 /home/yourname/traefik/letsencrypt/acme.json
docker compose restart
Issue 4: Can't Login to Dashboard
Symptoms: Keeps asking for password or shows "401 Unauthorized"
Causes:
-
Wrong password
- Try the original: username
admin, passwordchangethis
- Try the original: username
-
Password hash incorrect
- Regenerate:
echo $(htpasswd -nb admin yournewpassword)- Remember to use
$$instead of$in docker-compose.yml - Recreate:
docker compose up -d --force-recreate
Issue 5: Container Won't Start
Symptoms: docker ps doesn't show traefik container
Debug:
# Check what happened
docker logs traefik
# Common errors and solutions:
# "bind: address already in use"
# Something else is using port 80 or 443
sudo lsof -i :80
sudo lsof -i :443
# Kill the process or change the port
# "configuration error"
# Check YAML syntax
docker compose config
# Should show the parsed config with no errors
# "cannot find network"
# Recreate network
docker network create traefik-network
Getting Help
If you're stuck:
-
Check logs thoroughly:
docker logs traefik --tail 100 cat /home/yourname/traefik/logs/traefik.log -
Verify configuration:
cd /home/yourname/traefik docker compose config -
Test connectivity:
# Can the server reach the internet? curl -I https://google.com # Can you reach your server? ping yourdomain.com -
Check Traefik docs:
What We've Accomplished
Congratulations! You now have:
✅ A VPS with Docker installed ✅ Domain configured with DNS ✅ Traefik running in a Docker container ✅ Automatic SSL/TLS certificates from Let's Encrypt ✅ A secure dashboard to monitor your infrastructure ✅ Foundation for deploying unlimited applications
Understanding What's Running
Let's review the components:
Your Server
│
├─ Docker Engine
│ │
│ ├─ traefik-network (Docker network)
│ │ │
│ │ └─ traefik container
│ │ ├─ Listening on port 80 (HTTP)
│ │ ├─ Listening on port 443 (HTTPS)
│ │ ├─ Watching Docker socket for new containers
│ │ ├─ Managing Let's Encrypt certificates
│ │ └─ Serving dashboard at traefik.yourdomain.com
│
└─ File System
└─ /home/yourname/traefik/
├─ traefik.yml (config)
├─ docker-compose.yml (container definition)
├─ letsencrypt/acme.json (certificates)
└─ logs/ (log files)
Next Steps Preview
In Part 3: Deploying Your First Application, we'll:
- Deploy a simple static website
- Deploy a Next.js application
- Deploy a full-stack app (frontend + API + database)
- Understand Docker labels in depth
- Handle images and static assets
- Set up internal communication between services
But first, take a moment to appreciate what you've built. You have a production-ready reverse proxy with automatic HTTPS. Every application you deploy from now on will get:
- A clean subdomain URL
- Automatic SSL certificate
- Automatic HTTP → HTTPS redirect
- All in seconds
Maintenance and Monitoring
Daily/Weekly Checks
View running containers:
docker ps
Check Traefik logs for errors:
docker logs traefik --tail 100
Monitor dashboard:
- Visit
https://traefik.yourdomain.comregularly
Updates
Update Traefik when new versions release:
cd /home/yourname/traefik
# Pull latest image
docker compose pull
# Recreate container with new image
docker compose up -d
# Remove old images
docker image prune
Backups
What to back up:
# Configuration files
/home/yourname/traefik/traefik.yml
/home/yourname/traefik/docker-compose.yml
# Let's Encrypt certificates (important!)
/home/yourname/traefik/letsencrypt/acme.json
Simple backup script:
#!/bin/bash
BACKUP_DIR="/home/yourname/backups/traefik-$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR
cp -r /home/yourname/traefik $BACKUP_DIR
echo "Backup created at $BACKUP_DIR"
Security Considerations
Your setup is already quite secure, but here are additional hardening steps:
1. Use Strong Passwords
Change the default dashboard password:
echo $(htpasswd -nb admin a-very-strong-password-here)
# Update docker-compose.yml with the output
docker compose up -d --force-recreate
2. Limit Dashboard Access by IP (Optional)
Only allow dashboard access from specific IPs:
In docker-compose.yml, add:
- "traefik.http.middlewares.dashboard-ipwhitelist.ipwhitelist.sourcerange=YOUR.IP.ADDRESS.HERE/32"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth,dashboard-ipwhitelist"
3. Enable Fail2Ban
Protect SSH from brute force:
apt install -y fail2ban
systemctl enable fail2ban
systemctl start fail2ban
4. Keep System Updated
# Set up automatic security updates
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Performance Tips
1. Enable Compression
Add to traefik.yml:
entryPoints:
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
middlewares:
- compress@file
# Then create a middleware
http:
middlewares:
compress:
compress: {}
2. Monitor Resource Usage
# Check Docker stats
docker stats traefik
# Should use minimal CPU and ~50-100MB RAM
Advanced Topics (Preview)
In future parts, we'll cover:
- Custom SSL certificates (for private CAs)
- Multiple Let's Encrypt resolvers (DNS challenge for wildcards)
- Traefik Pilot (SaaS monitoring)
- Metrics and Prometheus integration
- High availability with multiple Traefik instances
- TCP/UDP routing (not just HTTP)
Conclusion
You've successfully set up Traefik! Your server is now ready to host multiple applications with automatic HTTPS.
This is the foundation. In Part 3, we'll start deploying actual applications and see the magic of Traefik's auto-discovery in action.
Take a break, explore your dashboard, and when you're ready:
Continue to Part 3: Deploying Your First Application
Questions or Issues? Double-check the Troubleshooting section above, and make sure all steps were followed in order.