Caddy is a web server that turns on HTTPS by itself. Give it a domain name in its config file, the Caddyfile, and it requests a certificate, renews it before it expires and redirects HTTP to HTTPS, with no certbot and no cron job. This guide installs Caddy from its official repository, serves a site over HTTPS and shows how to apply changes without downtime.
Before you start
- A Linux VPS running Ubuntu 22.04/24.04, Debian 12, or AlmaLinux/Rocky Linux 8 or 9.
- SSH access as a user with
sudo. - A domain, such as
example.com, with an A record pointing at the server IP, for example203.0.113.10. Caddy needs this before it can get a certificate. - Ports 80 and 443 open, and no other web server using them. Stop Apache or Nginx first if they are installed.
Step 1: Install Caddy
Ubuntu and Debian
Add Caddy's signing key and stable repository, then install:
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 caddyAlmaLinux and Rocky Linux
Caddy is published through a COPR repository:
sudo dnf install 'dnf-command(copr)'
sudo dnf copr enable @caddy/caddy
sudo dnf install caddy
sudo systemctl enable --now caddyThe package creates a caddy system user, a systemd service and a default config at /etc/caddy/Caddyfile. Check the version and the service:
caddy version
sudo systemctl status caddy --no-pagerNote: If these repository steps change, Caddy's official installation documentation has the current commands.
Step 2: Open the firewall
Caddy needs port 80 for certificate challenges and redirects, and 443 for HTTPS. With UFW on Ubuntu and Debian:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpWith firewalld on AlmaLinux and Rocky:
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reloadStep 3: Write a simple Caddyfile
Create a folder for the site and a test page:
sudo mkdir -p /var/www/example.com
echo '<h1>Served by Caddy</h1>' | sudo tee /var/www/example.com/index.htmlReplace the contents of /etc/caddy/Caddyfile with:
example.com, www.example.com {
root * /var/www/example.com
file_server
encode gzip zstd
log {
output file /var/log/caddy/example.com.log
}
}What each line does:
example.com, www.example.com: the site address. Because these are real domain names, Caddy turns on automatic HTTPS for both.- *`root `:** the folder files are served from.
file_server: serves static files from that folder.encode gzip zstd: compresses responses.log: writes an access log for the site. Make sure/var/log/caddyexists and is writable by thecaddyuser; the package creates it on most systems.
Add more sites by adding more blocks, one per domain.
Step 4: Validate and reload
Tidy the formatting and check the config before applying it:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/CaddyfileValid configuration means it is safe to load. Apply it without dropping connections:
sudo systemctl reload caddysystemctl reload caddy runs caddy reload under the hood with the right config path. If the new config is invalid, Caddy keeps running with the old one.
Warning: Avoid systemctl restart caddy for routine changes. A restart stops the server briefly, and a broken config would leave it stopped.
How automatic HTTPS works
When Caddy loads a site block with a public domain name, it:
- Requests a certificate from Let's Encrypt (or ZeroSSL as a fallback) using the HTTP or TLS-ALPN challenge on ports 80 and 443.
- Stores the certificate under the
caddyuser's data folder,/var/lib/caddy/.local/share/caddyon package installs. - Redirects all HTTP requests to HTTPS.
- Renews the certificate automatically well before it expires.
You do not need certbot. If you want to receive expiry warnings from the CA, add a global block with your email at the top of the Caddyfile:
{
email admin@example.com
}Check it works
- Watch Caddy obtain the certificate:
sudo journalctl -u caddy -fLook for certificate obtained successfully for your domain.
- Check the redirect and the HTTPS response:
curl -I http://example.com/
curl -I https://example.com/The first should return 308 with a Location: https://... header. The second should return 200 with Server: Caddy.
- Open
https://example.comin a browser and check the padlock.
Troubleshooting
- Caddy fails to get a certificate: the domain does not point at this server, or ports 80/443 are blocked. Check with
dig +short example.comand the firewall rules. address already in useon start: Apache or Nginx is running. Find it withsudo ss -ltnp | grep -E ':(80|443) 'and disable it.- Permission denied writing the log file: the
caddyuser cannot write to the log folder. Fix ownership withsudo chown caddy:caddy /var/log/caddy. - Changes do not appear: you edited a different file, or the reload failed validation. Run
caddy validateand checkjournalctl -u caddy.
Next steps
- Put apps behind Caddy: Use Caddy as a reverse proxy for apps.
- Add redirects, headers and caching: Caddy redirects, headers, gzip/zstd and caching.
- Fix certificate problems: Fix Caddy certificate errors and ACME failures.
- Run Caddy on your own Linux VPS.
