Deploying production web applications typically involves setting up reverse proxies, orchestrating ACME certificate bots, configuring auto-renewing timers, and manually tailoring SSL/TLS cipher suites. For administrators running Nginx or Apache, this workflow requires managing multiple configuration files and cron jobs. Caddy revolutionises this process. Written in Go as a modern, memory-safe, HTTP/2 and HTTP/3-native web server, Caddy provides Automatic HTTPS by default. Pointing Caddy to your domain name automatically procures, installs, and renews trusted Let’s Encrypt or ZeroSSL TLS certificates with zero manual intervention or external certbot scripts.
Why Choose Caddy on Ubuntu Server?
Understanding Caddy’s architectural benefits highlights why it has become an industry standard for cloud deployments:
- Automatic HTTPS by Default: Automatically handles ACME certificate issuance, OCSP stapling, and 90-day renewals in the background with zero administrative overhead.
- Human-Friendly Caddyfile Syntax: Replaces convoluted multi-line server blocks with concise, declarative directives (often requiring only 3 lines for a complete reverse proxy).
- HTTP/3 and QUIC Native Support: Out-of-the-box support for the fastest modern web transport protocols without needing third-party patches or custom modules.
- Zero-Downtime Reloads: Updates configurations dynamically using graceful API endpoints without dropping active client connections.
- Native Systemd Integration: Runs under dedicated system accounts with isolated file permissions and restart watchdogs.
Step 1: Install Caddy on Ubuntu 22.04 or 24.04 LTS
The official Caddy repository provides precompiled deb packages with verified GPG signatures:
1. Install Required Prerequisites
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
2. Import the Official GPG Key
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
3. Add the Stable APT Repository
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
4. Install Caddy
sudo apt update
sudo apt install -y caddy
5. Verify the Service Status
sudo systemctl status caddy
The output should indicate active (running). Caddy is now managing traffic on ports 80 and 443.
Step 2: Configure Firewall Rules (UFW)
For Caddy to obtain TLS certificates and serve traffic, your Ubuntu server must allow public HTTP and HTTPS access:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw reload
Note: Allowing UDP on port 443 enables native HTTP/3 QUIC communication.
Step 3: Serve Static Websites with Caddyfile
Caddy reads its primary configuration from /etc/caddy/Caddyfile. To serve a static website:
- Create your web root directory and assign ownership to the
caddyuser:sudo mkdir -p /var/www/example.com sudo chown -R caddy:caddy /var/www/example.com - Create a test HTML file:
echo "<h1>Welcome to Caddy on Ubuntu!</h1>" | sudo tee /var/www/example.com/index.html - Edit the Caddyfile:
sudo nano /etc/caddy/Caddyfile - Replace default contents with your domain block:
example.com { root * /var/www/example.com file_server encode gzip zstd } - Save the file and reload Caddy:
sudo systemctl reload caddy
Caddy immediately contacts Let’s Encrypt, completes the HTTP-01 challenge, provisions an SSL certificate, and serves your website over secure HTTPS.
Step 4: Configure a Secure Reverse Proxy for Node.js, Python, or Go Apps
If you are hosting web apps running on internal loopback ports (such as Docker, Next.js on port 3000, or FastAPI on port 8000), Caddy acts as an ultra-fast reverse proxy:
api.example.com {
reverse_proxy 127.0.0.1:3000
encode gzip zstd
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Caddy automatically forwards standard proxy headers (such as X-Forwarded-For and X-Forwarded-Proto) while terminating SSL at the server edge.
Step 5: Inspecting Automated Certificate Renewals
To verify certificate generation and check expiration health:
sudo journalctl -u caddy -n 50 --no-pager
The systemd journal log displays real-time ACME interactions, detailing domain challenge validations and automatic certificate installations stored securely under /var/lib/caddy/.local/share/caddy/certificates/.
Troubleshooting Common Caddy Issues
- Certificate Issuance Fails with “NXDOMAIN” or Timeout: Ensure your domain’s DNS A/AAAA records point directly to your Ubuntu server’s public IP address before starting Caddy. Let’s Encrypt servers must reach port 80 to complete domain ownership challenges.
- Port 80/443 Already in Use: If Apache or Nginx was previously installed, stop and disable them before running Caddy:
sudo systemctl stop nginx && sudo systemctl disable nginx. - Syntax Errors in Caddyfile: Before reloading a production server, validate your syntax without interrupting traffic using
caddy validate --config /etc/caddy/Caddyfile.