Skip to main content
Back to blog

Why I switched from Nginx to Caddy

·3 min readDevOps

I ran Nginx as my reverse proxy for about a year. It worked fine, but every time I added a new service, the same ritual played out: write a server block, configure SSL with Certbot, set proxy headers, test the config, reload Nginx. Repeat.

Caddy does all of that in two lines and handles HTTPS automatically. The switch took about 20 minutes and I have not missed Nginx once.

What Nginx required

A typical Nginx reverse proxy config for one service looked like this:

server {
    listen 80;
    server_name nextcloud.example.com;
    return 301 https://$server_name$request_uri;
}
 
server {
    listen 443 ssl;
    server_name nextcloud.example.com;
 
    ssl_certificate /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nextcloud.example.com/privkey.pem;
 
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Plus Certbot setup, cron jobs for renewal, and the occasional renewal failure that left a service without HTTPS until I noticed.

Multiply that by 8 services and you have a lot of configuration to maintain.

What Caddy requires

The equivalent Caddy config:

nextcloud.example.com {
    reverse_proxy localhost:8080
}

That is it. Caddy handles:

  • Obtaining the TLS certificate from Let's Encrypt
  • Automatic renewal before expiration
  • HTTP to HTTPS redirect
  • Proper proxy headers

For 8 services, my entire Caddyfile is about 30 lines compared to over 200 lines of Nginx config.

The migration

I listed all my Nginx server blocks, wrote the equivalent Caddyfile entries, stopped Nginx, started Caddy, and everything worked. The whole process was about 20 minutes, most of which was copying service names and port numbers.

Caddy obtained fresh certificates for all domains on first start. No Certbot migration needed.

What I lost

Fine-grained caching control. Nginx has more detailed caching directives. For my use case (just proxying), this does not matter.

Community size. Nginx has decades of Stack Overflow answers and tutorials. Caddy's documentation is good, but you find fewer third-party guides. This gap is closing as Caddy's popularity grows.

Muscle memory. I knew Nginx config syntax by heart. Learning Caddyfile syntax took about an hour of reading the docs.

What I gained

Zero certificate management. No Certbot, no renewal scripts, no monitoring for expiration. Certificates just work.

Readable configuration. I can look at my Caddyfile and understand what every service does in seconds. Nginx configs required mental parsing.

Faster iteration. Adding a new service is two lines and a reload. I self-host more services now because the overhead of exposing them is nearly zero.

If you are happy with Nginx and have everything automated, there is no urgent reason to switch. But if you are starting fresh or tired of managing certificates manually, Caddy is the better choice.

Sources

Enjoying the blog? Subscribe via RSS to get new posts in your reader.

Subscribe via RSS