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.

The Author

Koen Vervloesem has been writing about Linux and open source, computer security, privacy, programming, artificial intelligence, and the Internet of Things for more than 20 years. He holds Master's degrees of Engineering in Computer Science and Philosophy and teaches Linux, Python, and IoT classes. You can find more on his website at http://koen.vervloesem.eu.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus