« Previous 1 2 3 4
Run rootless Podman containers as systemd services
Power Up
Reverse Proxy Preparation
Before you create a container with Caddy [8] as a reverse proxy, first take a step back and review some prerequisites. The goal is to access each of the containers on its own subdomain of a domain you own. The reverse proxy will listen to all subdomains and forward HTTP requests to the corresponding container's hostname and port on the basis of the visited subdomain.
By default, Caddy obtains and renews TLS certificates automatically with Let's Encrypt [9], which is perfect for publicly accessible services. However, if you're running services on a local network and no one else should access them, it makes sense to create a wildcard certificate manually for your local domain, signed by your own Certificate Authority (CA). You then also define a wildcard domain in your local DNS server, such as *.services.home
, pointing to your container host's IP address and use this wildcard domain in your certificate.
I did both tasks with OPNsense [10], the software running on my router, because I'm using this CA for other hosts in my internal network, too. Alternatively, Caddy can generate a CA and sign certificates automatically for internal use. The details go beyond the scope of this article. You should change Caddy's configuration to suit your setup.
Caddy Reverse Proxy
Now, create the systemd unit file ~/.config/containers/systemd/caddy.container
with the content in Listing 2. This code makes Caddy listen on port 443 for HTTPS and connects it to the reverse proxy network, as well, so it can reach the FreshRSS container by way of its hostname. It also defines some volumes. Note that the volume for the certificates is read-only and shared with other containers (z,ro
).
Listing 2
caddy.container
01 [Unit] 02 Description=Caddy reverse proxy 03 04 [Container] 05 Image=docker.io/library/caddy:2.8.4 06 ContainerName=caddy 07 HostName=caddy 08 Volume=%h/containers/caddy/data/caddy:/data/caddy:Z 09 Volume=%h/containers/certificates:/data/certificates:z,ro 10 Volume=%h/containers/caddy/etc:/etc/caddy:Z 11 PublishPort=443:443 12 Network=reverse-proxy.network 13 14 [Service] 15 Restart=always 16 17 [Install] 18 WantedBy=default.target
Next, create the directory ~/containers/certificates
and add the certificate and key file for the wildcard domain. Also, create the directories ~/containers/caddy/data
and ~/containers/caddy/etc
. Within the /caddy/etc
directory, you should create a configuration file named Caddyfile
and give it the content:
*.services.home {tls /data/certificates/services.crt/data/certificates/services.key } freshrss.services.home { reverse_proxy freshrss }
This code uses the TLS certificate you created with common name *.services.home
for all your subdomains of services.home
. If freshrss.services.home
is requested, Caddy forwards the request to the freshrss
hostname on port 80.
Now reload the systemd daemon to make it aware of this new file and the changed freshrss
container unit file and (re)start the services for both containers:
$ systemctl --user daemon-reload $ systemctl --user restart freshrss $ systemctl --user start caddy
If all goes well, you should be able to visit FreshRSS in your web browser on https://freshrss.services.home. If not, have a look at the logs of Caddy's Podman container.
This setup can be readily expanded by adding new unit files for additional containers and appending entries in Caddy's configuration file to forward requests to the appropriate container. If a container listens on a port other than 80, just add the port to the reverse_proxy
line in the Caddyfile (e.g., reverse_proxy homepage:3000
). You can even add an authentication portal to your reverse proxy with the caddy-security
plugin.
Conclusion
Rootless Podman containers offer a secure method to operate services on a Linux system, which, combined with systemd unit files, makes it exceptionally flexible to configure. For instance, if one container depends on another, you can specify this arrangement with Requires=
and After=
in the [Unit]
section, just as you would for systemd services. Note that you need to mention the name of the generated service with the .service
extension. All other options in systemd unit files in the [Unit]
and [Service]
sections are supported, as well.
Additionally, you can simply query the status of a container with
systemctl status <container-name>.service
Upgrading a container is merely a matter of changing the version number in the container unit file, reloading the systemd daemon, and restarting the service. Personally, I transitioned an installation with Docker Compose on my home server to the approach outlined in this article some time ago, and I consider it a significant improvement.
Infos
- Docker Compose: https://docs.docker.com/compose/
- Podman: https://podman.io
- Podman Compose: https://github.com/containers/podman-compose
- Podman Quadlet: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html
- Fedora CoreOS: https://fedoraproject.org/coreos/
- openSUSE MicroOS: https://microos.opensuse.org
- FreshRSS: https://freshrss.org
- Caddy: https://caddyserver.com
- Let's Encrypt: https://letsencrypt.org
- OPNsense: https://opnsense.org
« Previous 1 2 3 4
Buy this article as PDF
(incl. VAT)