Run the Caddy server on Fedora using Podman

I heard many positive opinions about Caddy and Podman, so let’s try using them.

Why Caddy?

Caddy offers automatic HTTPS out-of-the-box, a straightforward configuration file syntax, support HTTP/2 and HTTP/3 by default, built-in security, and a modular design.

Why Podman?

Podman is daemonless, which simplifies container management and eliminates a potential central point of failure. Its native support for rootless containers enhances security by reducing the need for privileged operations.

Caddy

Let’s create directories to store the Caddy configuration, data, logs, and website content.

Terminal window
mkdir -p ~/containers/caddy/config
mkdir -p ~/containers/caddy/data
mkdir -p ~/containers/caddy/logs
mkdir -p ~/containers/caddy/sites/<site-name>

Below is an example of a Caddy configuration file for a single static site:

containers/caddy/config/Caddyfile
{
email <email-address>
}
<domain> {
root * /srv/<domain>
encode zstd gzip
header {
Cache-Control max-age=3600
}
file_server {
precompressed br gzip
}
handle_errors {
rewrite * /404.html
file_server
}
log {
format json
output file /var/log/caddy/<domain>_access.log {
roll true
roll_size 1gb
roll_keep 5
roll_keep_for 720h
}
}
}
  • It serves content from a specified directory.
  • It enables compression with Brotli, Zstandard, and gzip.
  • It sets a cache-control header with a maximum age of 1 hour.
  • It adds 404 handling by rewriting requests to a custom error page.
  • It writes logs to an access file with automatic rotation.

Now it’s time to run the server.

Podman

We can use Podlet to generate a Podman Quadlet file from a Podman command.

Terminal window
sudo dnf install podman podlet

Note that when using rootless containers, ports 80 and 443 cannot be used directly because ports below 1024 are restricted to privileged users. There are several options:

  • Run Podman as root (which is not preferred).
  • Grant low-numbered port access to Podman (for example, using a tool like setcap, sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary).
  • Expose a privileged port through systemctl by adjusting system settings (like adding net.ipv4.ip_unprivileged_port_start=443 to /etc/sysctl.conf)
  • Redirect a non-privileged port to ports 80 and 443 using iptables or firewalld.

There are probably more options available, but let’s choose the last one. For example, we can redirect port 1880 to 80 and port 1443 to 443 using firewalld.

Terminal window
sudo systemctl enable firewalld
sudo systemctl start firewalld
sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=1443
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=1880
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Generate quadlet caddy.container file.

Terminal window
podlet --file . --install --description Caddy podman run --name caddy --restart always -p 1880:80 -p 1443:443 -v ~/containers/caddy/config:/etc/caddy:ro -v ~/containers/caddy/sites:/srv:ro -v ~/containers/caddy/data:/data -v ~/containers/caddy/logs:/var/log/caddy docker.io/library/caddy:2.9.1

It will produce:

[Unit]
Description=Caddy
[Container]
ContainerName=caddy
Image=docker.io/library/caddy:2.9.1
PublishPort=1880:80
PublishPort=1443:443
Volume=/home/<user>/containers/caddy/config:/etc/caddy:ro
Volume=/home/<user>/containers/caddy/sites:/srv:ro
Volume=/home/<user>/containers/caddy/data:/data
Volume=/home/<user>/containers/caddy/logs:/var/log/caddy
[Service]
Restart=always
[Install]
WantedBy=default.target

Move it to the appropriate directory.

Terminal window
mkdir -p .config/containers/systemd
mv caddy.container .config/containers/systemd/

Reload the systemd daemon, and start the server.

Terminal window
systemctl --user daemon-reload
systemctl --user start caddy

In case of an error, you can check the status of the service or view detailed log output using

Terminal window
systemctl --user status caddy.service

and

Terminal window
journalctl --user -xeu caddy.service

commands.

By default, the service stops when a user logs out. To prevent this, enable lingering for your user account. Lingering allows your user services to continue running even when you’re not logged in.

Terminal window
loginctl enable-linger <user>

That’s all. The server will run, serve the files you store in the designated site folder, and automatically start on system boot. This is a basic configuration, so please adjust it to suit your needs.