How to Install Caddy on Ubuntu 26.04

By 

Published on

8 min read

Caddy server stack with Ubuntu 26.04 and automatic HTTPS

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

TaskCommand
Install Caddysudo apt install caddy
Edit configurationsudo nano /etc/caddy/Caddyfile
Reload configurationsudo systemctl reload caddy
Restart Caddysudo systemctl restart caddy
Check statussudo systemctl status caddy
Validate Caddyfilesudo caddy validate --config /etc/caddy/Caddyfile
Format Caddyfilesudo caddy fmt --overwrite /etc/caddy/Caddyfile
View logssudo journalctl -u caddy -f

Prerequisites

Before you start, make sure you have:

  • An Ubuntu 26.04 server with a user that has sudo access.
  • 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:

Terminal
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Caddy supports HTTP/3 over UDP. Allow UDP port 443 if you want clients to use it:

Terminal
sudo ufw allow 443/udp

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

Terminal
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

Add the Caddy signing key:

Terminal
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

Add the Caddy APT repository:

Terminal
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list

Make both repository files readable by APT:

Terminal
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list

Update the package index and install Caddy:

Terminal
sudo apt update
sudo apt install -y caddy

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

Terminal
caddy version

Next, confirm that the service is running:

Terminal
sudo systemctl status caddy

The status output should show active (running). Press q to exit the status screen.

You can also request the default site from the server:

Terminal
curl -I http://localhost

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

Terminal
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.backup

Next, open the Caddyfile:

Terminal
sudo nano /etc/caddy/Caddyfile

Replace the contents with the following site block. Change example.com to your domain:

/etc/caddy/Caddyfilecaddy
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:

Terminal
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.com

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

Terminal
sudo caddy validate --config /etc/caddy/Caddyfile

If the command does not report an error, reload Caddy:

Terminal
sudo systemctl reload caddy

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

/etc/caddy/Caddyfilecaddy
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:

/etc/caddy/Caddyfilecaddy
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:

Terminal
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

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

/etc/caddy/Caddyfilecaddy
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:

/etc/caddy/Caddyfilecaddy
import /etc/caddy/sites/*.caddy

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

Terminal
sudo caddy validate --config /etc/caddy/Caddyfile

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

Terminal
sudo caddy fmt --overwrite /etc/caddy/Caddyfile

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

Terminal
sudo systemctl start caddy
sudo systemctl stop caddy
sudo systemctl restart caddy
sudo systemctl reload caddy
sudo systemctl status caddy

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

Terminal
sudo systemctl enable caddy

View Caddy Logs

Caddy writes its runtime and service logs to the systemd journal. Follow new entries with journalctl :

Terminal
sudo journalctl -u caddy -f

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

/etc/caddy/Caddyfilecaddy
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:

Terminal
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy

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

Terminal
sudo apt update
sudo apt install --only-upgrade caddy

The package upgrade keeps your Caddyfile and Caddy data under /var/lib/caddy. After an update, confirm the installed version and service status:

Terminal
caddy version
sudo systemctl status caddy

Troubleshooting

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

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