How to Install Caddy on Ubuntu 26.04

When you publish a website or put an application behind a reverse proxy, configuring HTTPS can take longer than installing the web server. Caddy handles certificate issuance, renewal, and HTTP-to-HTTPS redirects automatically for most deployments.
Once your domain points to the server, a short Caddyfile is often enough to serve the site over HTTP/2 and HTTP/3. This guide explains how to install Caddy on Ubuntu 26.04 from the official APT repository, serve a static site, configure a reverse proxy, and manage the service with systemd.
Quick Reference
| Task | Command |
|---|---|
| Install Caddy | sudo apt install caddy |
| Edit configuration | sudo nano /etc/caddy/Caddyfile |
| Reload configuration | sudo systemctl reload caddy |
| Restart Caddy | sudo systemctl restart caddy |
| Check status | sudo systemctl status caddy |
| Validate Caddyfile | sudo caddy validate --config /etc/caddy/Caddyfile |
| Format Caddyfile | sudo caddy fmt --overwrite /etc/caddy/Caddyfile |
| View logs | sudo journalctl -u caddy -f |
Prerequisites
Before you start, make sure you have:
- An Ubuntu 26.04 server with a user that has
sudoaccess. - A domain name with an A or AAAA record pointing to the server if you want a publicly trusted HTTPS certificate.
- TCP ports 80 and 443 open in the server firewall and any provider-level firewall.
If you use UFW , allow HTTP and HTTPS traffic with these commands:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpCaddy supports HTTP/3 over UDP. Allow UDP port 443 if you want clients to use it:
sudo ufw allow 443/udpIf you only want to test Caddy locally, use an explicit HTTP address such as http://localhost or a non-standard port such as :8080 in the Caddyfile. Caddy will not activate automatic HTTPS for that site address.
Install Caddy from the Official Repository
The Caddy project maintains an official APT repository on Cloudsmith. We will use it to install the current stable release and receive future Caddy updates through APT.
Install the prerequisite packages:
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curlAdd the Caddy signing key:
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgAdd the Caddy APT repository:
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.listMake both repository files readable by APT:
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.listUpdate the package index and install Caddy:
sudo apt update
sudo apt install -y caddyThe package installs the binary at /usr/bin/caddy, creates the caddy system user, adds a default configuration at /etc/caddy/Caddyfile, and installs the caddy.service systemd unit.
Display the installed version:
caddy versionNext, confirm that the service is running:
sudo systemctl status caddyThe status output should show active (running). Press q to exit the status screen.
You can also request the default site from the server:
curl -I http://localhostA working installation returns an HTTP 200 OK response and includes Server: Caddy in the headers. You can also open the server’s IP address in a browser to view the default page.
Serve a Static Site
Caddy reads its configuration from /etc/caddy/Caddyfile. The default file serves content from /usr/share/caddy on port 80.
Before changing it, create a backup:
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.backupNext, open the Caddyfile:
sudo nano /etc/caddy/CaddyfileReplace the contents with the following site block. Change example.com to your domain:
example.com {
root * /var/www/example.com
file_server
}The domain on the first line is the site address. Caddy uses it to obtain a certificate, redirect HTTP requests to HTTPS, and serve the site securely. You do not need a separate certificate block or listen directive.
Create the document root and add a test page:
sudo mkdir -p /var/www/example.com
echo "<h1>Hello from Caddy</h1>" | sudo tee /var/www/example.com/index.html
sudo chmod -R a+rX /var/www/example.comThe caddy service only needs read access to static site files. Keeping the files owned by your deployment user or by root prevents the web server process from changing them.
Before applying the new configuration, validate it:
sudo caddy validate --config /etc/caddy/CaddyfileIf the command does not report an error, reload Caddy:
sudo systemctl reload caddyThe reload command applies the new Caddyfile without stopping the service. Caddy then requests a certificate and redirects HTTP traffic to HTTPS. If DNS points to the server and ports 80 and 443 are reachable, certificate issuance usually completes within a few seconds.
Add a Reverse Proxy
Caddy is often used in front of an application running on the same server. For example, if a Node.js or Python application listens on 127.0.0.1:3000, use this Caddyfile:
app.example.com {
reverse_proxy 127.0.0.1:3000
}With this configuration, Caddy handles HTTPS and forwards requests to the application. It also passes the incoming Host header, sets the common X-Forwarded-* headers, and supports WebSocket connections without extra directives.
To proxy a subpath, scope the directive:
example.com {
root * /var/www/example.com
file_server
handle /api/* {
reverse_proxy 127.0.0.1:3000
}
}Requests under /api/ are sent to the application with the /api prefix unchanged. Other requests continue to use the static file server. If the application expects /users instead of /api/users, replace handle with handle_path to remove the matching prefix before proxying the request.
Validate and reload the configuration after making the change:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddyIf you also work with Nginx, see our Nginx reverse proxy guide for the equivalent configuration.
Host Multiple Sites
You can host several domains and subdomains from the same Caddyfile. Add one site block for each hostname:
example.com {
root * /var/www/example.com
file_server
}
blog.example.com {
root * /var/www/blog
file_server
encode gzip
}
api.example.com {
reverse_proxy 127.0.0.1:8080
}Caddy obtains and renews a certificate for each eligible hostname. Unlike an Nginx sites-available setup, this configuration does not require a separate symlink step.
As the number of sites grows, you can split the configuration across files with the import directive:
import /etc/caddy/sites/*.caddyCreate the /etc/caddy/sites directory, then add one .caddy file for each site. Run caddy validate after adding or removing an imported file.
Validate and Format the Caddyfile
Before every reload, validate the Caddyfile:
sudo caddy validate --config /etc/caddy/CaddyfileValidation parses the file and provisions its modules without starting another server. If Caddy finds a syntax or configuration error, it reports the affected line or directive.
Caddy also includes a formatter for indentation and spacing:
sudo caddy fmt --overwrite /etc/caddy/CaddyfileThe --overwrite option replaces the file with the formatted version. Review the file or keep it in version control if you want to inspect formatting changes before deployment.
Manage Caddy with systemd
The package runs Caddy as a systemd
service named caddy.service. Use these commands to control it:
sudo systemctl start caddy
sudo systemctl stop caddy
sudo systemctl restart caddy
sudo systemctl reload caddy
sudo systemctl status caddyFor Caddyfile changes, use reload instead of restart. Reloading applies the new configuration through Caddy’s admin API without interrupting active connections.
The package normally enables the service during installation. If it was disabled, enable it again with:
sudo systemctl enable caddyView Caddy Logs
Caddy writes its runtime and service logs to the systemd journal. Follow new entries with journalctl :
sudo journalctl -u caddy -fAccess logging is separate and is not enabled by default. To write JSON access logs to a file, add a log directive to the site block:
example.com {
root * /var/www/example.com
file_server
log {
output file /var/log/caddy/access.log
format json
}
}Create the log directory once with the correct ownership:
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddyReload Caddy after validating the change. Caddy rotates file-based access logs by default, and JSON output can be read by log collection tools such as Vector, Loki, or Elasticsearch.
Update Caddy
Because Caddy was installed from an APT repository, you can update it with the normal package workflow:
sudo apt update
sudo apt install --only-upgrade caddyThe package upgrade keeps your Caddyfile and Caddy data under /var/lib/caddy. After an update, confirm the installed version and service status:
caddy version
sudo systemctl status caddyTroubleshooting
bind: address already in use on port 80 or 443
Another web server (often Apache or a previous Nginx install) is already listening on the port. Stop the conflicting service with sudo systemctl stop apache2 or sudo systemctl stop nginx, then start Caddy.
Certificate issuance fails
Caddy needs inbound access on port 80 for the HTTP challenge or port 443 for the TLS-ALPN challenge. Confirm the firewall allows both, that every configured A or AAAA record points at this server, and that no other proxy intercepts the validation request. The journal log (journalctl -u caddy) shows the exact ACME error.
permission denied reading /var/www/...
Caddy runs as the caddy user, not as root. Files under the document root must be readable, and parent directories must be executable. Run sudo chmod -R a+rX /var/www/example.com, then check each parent directory if the error continues. Avoid giving the service account ownership of files that it only needs to serve.
Caddyfile validates but the site is unreachable
Check that the site block uses the exact hostname requested by the browser. Test DNS with getent hosts example.com, then use curl -v https://example.com to inspect the connection and response.
HTTP requests are not redirected to HTTPS
Automatic HTTPS is disabled when a site address starts with http:// or contains only an HTTP port. Use example.com rather than http://example.com in the Caddyfile, validate the configuration, and inspect the journal for certificate errors.
Conclusion
Once Caddy is running, most day-to-day work happens in /etc/caddy/Caddyfile. Validate each change before reloading, and keep ports 80 and 443 reachable so Caddy can issue certificates and redirect visitors to HTTPS.
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page