Install Caddy web server with automatic HTTPS on VPS

Install Caddy Web Server on VPS (Auto HTTPS)

June 2026 · 7 min read

Caddy is a modern web server that stands out for its key feature: automatically requesting and renewing SSL certificates from Let's Encrypt without requiring certbot or cron jobs. Simply point your domain to your VPS and write a short config file to get HTTPS working immediately. Perfect for developers who want to deploy websites or reverse proxies quickly without managing certificates manually. This article covers everything from installation to reverse proxy setup and multi-website configuration.

Install Caddy

# Ubuntu / Debian (official repo)
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 -y caddy

# Check if it's running
systemctl status caddy
caddy version

Serve Static Website with Auto HTTPS

Edit the /etc/caddy/Caddyfile to this simple configuration — Caddy will automatically request a certificate once the domain points correctly:

example.com {
    root * /var/www/example
    file_server
    encode gzip zstd
}

# reload config
sudo systemctl reload caddy
Note: Before reloading, ensure the A record for example.com points to your VPS IP and ports 80 + 443 are open. Caddy needs to complete the HTTP/TLS challenge with Let's Encrypt to issue the certificate successfully.

Reverse Proxy to Applications (Node / PHP / Python)

For applications running on internal ports (e.g., Node on 3000 or PHP-FPM) — Caddy can proxy with HTTPS in just a few lines:

app.example.com {
    reverse_proxy localhost:3000
}

api.example.com {
    reverse_proxy localhost:8000 {
        header_up X-Real-IP {remote_host}
    }
}

Multiple Websites in One File and Redirects

# www -> non-www
www.example.com {
    redir https://example.com{uri} permanent
}

example.com {
    root * /var/www/example
    file_server
    # compression + security headers
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options nosniff
    }
}

blog.example.com {
    reverse_proxy localhost:2368   # Ghost
}

After setting up a new domain, verify that DNS, MX records, and SSL chain are correct before going live. Tools like dnsxray.com help check domain health and SSL validity in one place. And if you need a VPS in Thailand or Singapore with Caddy pre-installed, asiagb.com offers full root access with Thai language support.


Verify Caddy is Working Correctly

After setting up your Caddyfile, confirm that the config is valid, the certificate was issued, and your website responds over HTTPS:

# 1. Validate Caddyfile syntax before reload
caddy validate --config /etc/caddy/Caddyfile

# 2. Check if service is running
systemctl status caddy

# 3. Test HTTPS and view the auto-issued certificate
curl -I https://example.com
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -dates

# 4. View certificate issuance logs
journalctl -u caddy --since "10 min ago" | grep -i certificate
Tip: Caddy stores certificates in /var/lib/caddy/.local/share/caddy — back up this directory to preserve rate limits if you migrate servers (Let's Encrypt limits 50 certificates per domain per week).

Common Issues & Troubleshooting

Error: Certificate request failed "challenge failed"

In most cases, the domain is not pointing to your VPS or the firewall is blocking ports 80/443. Check this way:

dig +short example.com        # should return your VPS IP
sudo ufw allow 80,443/tcp
sudo ss -tlnp | grep -E ':80|:443'

Error: "address already in use" port 80

Apache or Nginx is occupying port 80. Stop them first or configure one of them to use a different port:

sudo systemctl stop apache2 nginx
sudo systemctl disable apache2 nginx

Config doesn't update after reload

Make sure you edited the correct file (/etc/caddy/Caddyfile) and caddy validate passes. If there's a syntax error, Caddy keeps the old config and logs the error in the journal.

Certificate issued but showing "rate limited"

You've tested too frequently and Let's Encrypt has temporarily blocked you. During testing, use the staging endpoint first, then switch to production:

# In global block of Caddyfile
{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

Frequently Asked Questions

Is Caddy better than Nginx?

Depends on your use case. Caddy wins on simplicity and auto HTTPS — ideal for quick deployments and small website counts. Nginx still excels in advanced tuning, massive traffic volumes, and broader plugin ecosystem. Many teams use Caddy for dev/staging and Nginx for large-scale production.

Does Caddy consume a lot of RAM?

Not much. Built in Go as a single binary, Caddy idles around 20-40 MB — perfect for 1 GB RAM VPS. If you enable on-demand TLS for hundreds of domains, consider allocating extra memory.

Does SSL auto-renewal really work without cron jobs?

Yes, no cron needed. Caddy checks and renews certificates automatically in the background before expiration (~30 days prior) as long as the service runs and your domain still points correctly.