Lesson 15 of 21

Install Caddy

Monthly cost: $0 Expected time: ~15–20 minutes

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:

  1. Install Nginx
  2. Write a config file
  3. Install Certbot separately
  4. Run Certbot to get a certificate
  5. Set up auto-renewal
  6. 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

When You're Done

Further Reading