What Is Caddy and Why Not Nginx
Caddy is a modern web server that does what Nginx does but with one killer feature: automatic HTTPS. When you point a domain at Caddy, it gets a Let's Encrypt certificate, configures HTTPS, and renews the certificate — all without you doing anything.
With Nginx, you'd need to:
- Install Nginx
- Write a config file
- Install Certbot separately
- Run Certbot to get a certificate
- Set up auto-renewal
- Debug it when renewal breaks
With Caddy, you write one line and HTTPS just works. For a curriculum that wants people to succeed on their first try, that's a massive win.
Step 1: Install Caddy
SSH into your server:
ssh claw@YOUR_SERVER_IP
Install Caddy from the official repository:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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 -y
Verify it installed:
caddy version
Step 2: Open Firewall Ports
Caddy needs ports 80 (HTTP) and 443 (HTTPS):
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Step 3: Create Your Caddyfile
The Caddyfile is where you define all your sites. One file, all services:
sudo nano /etc/caddy/Caddyfile
# OpenClaw web interface
claw.yourdomain.com {
reverse_proxy localhost:3838
}
# Gitea
git.yourdomain.com {
reverse_proxy localhost:3000
}
# Curriculum site (static files)
learn.yourdomain.com {
root * /home/claw/openclaw-vps-curriculum
file_server
}
That's it. No SSL config. No certificate commands. Caddy handles all of it.
Step 4: Start Caddy
sudo systemctl restart caddy
sudo systemctl enable caddy
Check status:
sudo systemctl status caddy
Visit any of your domains — they should load over HTTPS with a valid certificate.
Step 5: Restrict Access to Private Services
Not everything should be public. Your Gitea and OpenClaw web interface should be restricted. Caddy makes this easy with IP allowlists or basic auth.
Option A: IP Allowlist (Recommended)
Only allow your home IP and the VPS itself:
git.yourdomain.com {
@blocked not client_ip YOUR_HOME_IP 127.0.0.1 ::1
abort @blocked
reverse_proxy localhost:3000
}
claw.yourdomain.com {
@blocked not client_ip YOUR_HOME_IP 127.0.0.1 ::1
abort @blocked
reverse_proxy localhost:3838
}
Replace YOUR_HOME_IP with your actual IP (find it at whatismyip.com).
The abort directive silently drops the connection — outsiders don't even get an error page.
Option B: Basic Auth (If Your IP Changes Often)
Create a password hash:
caddy hash-password
Type your password, copy the hash.
git.yourdomain.com {
basic_auth {
yourusername $2a$14$THE_HASH_FROM_ABOVE
}
reverse_proxy localhost:3000
}
This prompts for a username/password before showing the site.
Option C: Both (Belt and Suspenders)
git.yourdomain.com {
@allowed client_ip YOUR_HOME_IP 127.0.0.1 ::1
handle @allowed {
reverse_proxy localhost:3000
}
handle {
basic_auth {
yourusername $2a$14$HASH
}
reverse_proxy localhost:3000
}
}
This lets you in without a password from home, and requires a password from anywhere else.
Step 6: Keep Public Sites Public
The curriculum site should be accessible to everyone — no restrictions:
learn.yourdomain.com {
root * /home/claw/openclaw-vps-curriculum
file_server
# Cache static assets
@static path *.css *.js *.png *.jpg *.svg *.ico *.woff2
header @static Cache-Control "public, max-age=604800, immutable"
}
Step 7: Close Raw Ports
Now that Caddy proxies everything, close the raw service ports:
sudo ufw delete allow 3000/tcp # Gitea was on 3000
sudo ufw delete allow 3838/tcp # OpenClaw was on 3838
Services are now only accessible through Caddy — over HTTPS, with access control.
Reloading After Config Changes
Whenever you edit the Caddyfile:
sudo systemctl reload caddy
Caddy validates the config before applying — if there's a syntax error, the old config stays active. No downtime.
Troubleshooting
- Certificate not working: Make sure DNS A records point to your server IP and ports 80/443 are open
- 502 Bad Gateway: The backend service isn't running — check
docker ps - Connection refused: Check
sudo ufw status— is the port allowed? - Check logs:
sudo journalctl -u caddy --no-pager -n 50
When You're Done
- Caddy installed and running
- Caddyfile configured for all services (OpenClaw, Gitea, curriculum site)
- All sites loading over HTTPS with valid certificates
- Private services (Gitea, OpenClaw) restricted via IP allowlist or basic auth
- Public site (curriculum) accessible to everyone
- Raw service ports closed
- Know how to reload Caddy after config changes