Websites & apps

How to install Caddy web server with automatic HTTPS

Install Caddy from its official package repository on Ubuntu, Debian, AlmaLinux or Rocky Linux, write a simple Caddyfile, and let Caddy get and renew Let's Encrypt certificates for you.

BeginnerLinux server5 min read

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 example 203.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:

Bash
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

AlmaLinux and Rocky Linux

Caddy is published through a COPR repository:

Bash
sudo dnf install 'dnf-command(copr)'
sudo dnf copr enable @caddy/caddy
sudo dnf install caddy
sudo systemctl enable --now caddy

The package creates a caddy system user, a systemd service and a default config at /etc/caddy/Caddyfile. Check the version and the service:

Bash
caddy version
sudo systemctl status caddy --no-pager

Note: 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:

Bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

With firewalld on AlmaLinux and Rocky:

Bash
sudo firewall-cmd --permanent --add-service=http --add-service=https
sudo firewall-cmd --reload

Step 3: Write a simple Caddyfile

Create a folder for the site and a test page:

Bash
sudo mkdir -p /var/www/example.com
echo '<h1>Served by Caddy</h1>' | sudo tee /var/www/example.com/index.html

Replace 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/caddy exists and is writable by the caddy user; 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:

Bash
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile

Valid configuration means it is safe to load. Apply it without dropping connections:

Bash
sudo systemctl reload caddy

systemctl 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:

  1. Requests a certificate from Let's Encrypt (or ZeroSSL as a fallback) using the HTTP or TLS-ALPN challenge on ports 80 and 443.
  2. Stores the certificate under the caddy user's data folder, /var/lib/caddy/.local/share/caddy on package installs.
  3. Redirects all HTTP requests to HTTPS.
  4. 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

  1. Watch Caddy obtain the certificate:
Bash
   sudo journalctl -u caddy -f

Look for certificate obtained successfully for your domain.

  1. Check the redirect and the HTTPS response:
Bash
   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.

  1. Open https://example.com in 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.com and the firewall rules.
  • address already in use on start: Apache or Nginx is running. Find it with sudo ss -ltnp | grep -E ':(80|443) ' and disable it.
  • Permission denied writing the log file: the caddy user cannot write to the log folder. Fix ownership with sudo chown caddy:caddy /var/log/caddy.
  • Changes do not appear: you edited a different file, or the reload failed validation. Run caddy validate and check journalctl -u caddy.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.