description: Simplify your home lab or VPS setup with Caddy's zero-config automatic HTTPS and dead-simple reverse proxy configuration
tags: homelab, devops, webdev, tutorial
If you're running multiple services on your home lab or VPS, manually managing HTTPS certificates and reverse proxy configurations can become tedious fast. Caddy is a web server that automatically obtains and renews Let's Encrypt certificates with zero configuration, making it perfect for exposing your services securely.
Why Caddy Over nginx or Apache
I switched to Caddy after spending years with nginx, and the difference is striking. With nginx, you need Certbot, cron jobs for renewal, and careful configuration management. With Caddy, you literally just specify the domain name and it handles everything. The configuration syntax is also dramatically simpler—what takes 50 lines in nginx takes 5 in Caddy.
Installing Caddy
On Ubuntu/Debian:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Caddy will automatically start as a systemd service.
Basic Reverse Proxy Configuration
Create or edit /etc/caddy/Caddyfile:
# Simple reverse proxy to a local service
myapp.example.com {
reverse_proxy localhost:3000
}
# Proxy with custom headers
api.example.com {
reverse_proxy localhost:8080 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
header_up X-Forwarded-Proto {scheme}
}
}
# Multiple services on different paths
example.com {
reverse_proxy /api/* localhost:8080
reverse_proxy /web/* localhost:3000
reverse_proxy /* localhost:8081
}
That's it. No SSL configuration needed. Caddy automatically:
- Obtains Let's Encrypt certificates
- Redirects HTTP to HTTPS
- Renews certificates before expiry
- Serves HTTPS on port 443
Real-World Example: Home Lab Setup
Here's my actual Caddyfile for my home lab services:
# Jellyfin media server
jellyfin.mydomain.com {
reverse_proxy localhost:8096
}
# Home Assistant
home.mydomain.com {
reverse_proxy localhost:8123
}
# Grafana monitoring
grafana.mydomain.com {
reverse_proxy localhost:3001
}
# Portainer container management
portainer.mydomain.com {
reverse_proxy localhost:9000 {
transport http {
tls_insecure_skip_verify
}
}
}
# Catch-all for root domain
mydomain.com {
respond "Home Lab Services" 200
}
After editing the Caddyfile, reload Caddy:
sudo systemctl reload caddy
Watch the logs to see certificates being obtained:
sudo journalctl -u caddy -f
You'll see Caddy automatically reaching out to Let's Encrypt and obtaining certificates for each domain.
Advanced Configuration: Docker Container Backend
If your services run in Docker, you can proxy to them by container name when Caddy is on the same Docker network:
version: '3.8'
services:
caddy:
image: caddy:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
networks:
- webproxy
myapp:
image: myapp:latest
networks:
- webproxy
networks:
webproxy:
driver: bridge
volumes:
caddy_data:
caddy_config:
Then in your Caddyfile:
myapp.example.com {
reverse_proxy myapp:3000
}
Handling Websockets
Many modern apps use WebSockets. Caddy handles them automatically with no extra configuration:
chat.example.com {
reverse_proxy localhost:3000
}
That's it. Unlike nginx where you need specific upgrade headers, Caddy detects and handles WebSocket connections automatically.
Load Balancing Multiple Backends
If you're running multiple instances of a service:
api.example.com {
reverse_proxy localhost:8001 localhost:8002 localhost:8003 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}
Caddy will distribute requests across backends and automatically remove unhealthy ones from rotation.
Security Headers
Add security headers easily:
example.com {
header {
Strict-Transport-Security "max-age=31536000;"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "no-referrer-when-downgrade"
}
reverse_proxy localhost:3000
}
Authentication Layer
Add basic auth in front of any service:
admin.example.com {
basicauth {
admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4wHaNzfF29C
}
reverse_proxy localhost:9000
}
Generate the hash with:
caddy hash-password
Common Gotchas
Port 80/443 Already in Use: If you have Apache or nginx running, stop them first. Only one process can listen on these ports.
DNS Not Pointing to Server: Caddy needs to complete the ACME challenge. Your domain must resolve to your server's public IP before Caddy can obtain certificates.
Firewall Blocking: Ensure ports 80 and 443 are open in your firewall:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
IPv6 Issues: If you see IPv6 errors but only use IPv4:
{
auto_https disable_redirects
}
myapp.example.com {
bind 0.0.0.0
reverse_proxy localhost:3000
}
Performance Considerations
Caddy is written in Go and is very efficient, but for high-traffic scenarios, tune these settings:
{
servers {
timeouts {
read_body 10s
read_header 5s
write 30s
idle 120s
}
}
}
What This Replaced
Before Caddy, my nginx setup required:
- Initial nginx configuration (30+ lines per service)
- Certbot installation and configuration
- Systemd timer or cron job for renewal
- Manual renewal testing
- Separate configuration for HTTP->HTTPS redirects
- Regular maintenance to ensure renewals work
With Caddy, I have a 3-line config per service and zero maintenance. I've been running it for two years and have never thought about certificates once.
Getting Started
Start small. Pick one service you're currently accessing via HTTP or with a self-signed certificate. Add a 3-line Caddy config for it, point the DNS, and watch it work. Once you see how trivial it is, you'll migrate everything else quickly.
The combination of automatic HTTPS and simple configuration makes Caddy the obvious choice for home labs and small-scale deployments. It's one of those tools that makes you wonder how you ever did it the old way.
What's your go-to reverse proxy? Have you tried Caddy? Drop a comment below!
Top comments (0)