Podman Quadlets

Docker alternative Podman has long been a favorite of mine. It runs rootless out of the box. It handles secrets with ease. Most importantly to me, its network back end “Pasta” enables true IP recognition within containers, which Docker struggles to perform without relying on the host network.

Deployment of containers is a different story. Docker offers its compose tool to quickly spin up and modify containers using a portable configuration file. While a compose plugin is available for Podman, the official methodology involves dabbling with systemd integration using what are known as Quadlets.

My goal with this write-up is to briefly lay out Podman Quadlets. You’ll find they aren’t too different from the average compose file. Let's go over the basics.

(Disclaimer: this guide assumes you are already familiar with Docker or Podman. This is more complicated than using compose. I am not an expert on the subject matter. I just think they're neat.)

First, make a new unprivileged user to run your containers, then run the following command to ensure containers run while a user is not actively logged in:

sudo loginctl enable-linger <podman user>

Log in as your new user. Create a directory at the following location. We'll be storing our quadlets as .container files here.

~/.config/containers/systemd

Using your editor of choice, create a file in this directory with the preferred name of your container and give it the extension .container, like this:

~/.config/containers/systemd/example.container

Alright, now down to business. Here is an example of how your quadlet should be structured:

[Unit]
Description=
After=

[Container]
Image=
Environment=
Volume=
PublishPort=

[Service]
Restart=

[Volume]
VolumeName=

[Install]
WantedBy=

If you've worked with Docker Compose, some of these entries should look familiar. Let's break this down piece by piece.

The [Unit] section is where you can declare some basic information about your container, such as a description. You can optionally use "After=" to ensure this container starts after a different system process or container. Let's use Linuxserver's Nextcloud image as an example.

[Unit]
Description=My Nextcloud
After=mariadbnextcloud.container redisnextcloud.container

The [Container] section holds most of what you would specify in a compose file. "Image=" is where you will declare what image you are using- note that Podman requires the prefix of "docker.io/" when using images from Docker Hub. Environment variables and volumes can be declared under "Environment=" and "Volume=" respectively, and "PublishPort=" is where you will specify any ports you would like to expose. There's lots more you can put here- check out the documentation for more examples.

[Container]
Image=lscr.io/linuxserver/nextcloud:latest
Network=pasta
Environment=PUID=1000
Environment=GUID=1000
Environment=America/Detroit
Environment=REDIS_HOST=localhost
Environment=REDIS_PORT=6379
Volume=nextcloud_data:/config
Volume=/evimedia/nextcloud:/evimedia/nextcloud
PublishPort=127.0.0.1:6443:443

(Quick note: I expose my ports on localhost and use Pasta's configuration to allow my reverse proxy to access them, but that's outside the scope of this guide. I might do a separate write-up on that.)

Let's move on to [Service]. You can specify "restart=always" to ensure your containers automatically restart if they fail for some reason.

[Service]
Restart=always

Just as Docker Compose requires you to declare volumes after specifying container information, you must do so under the [Volume] section as well. This is entirely optional and only necessary if you are using dedicated volumes as opposed to attaching a directory on your system.

[Volume]
VolumeName=nextcloud_data

This leaves us with the [Install] section. I won't go into detail here, but the following will ensure the service starts at an ideal time on boot:

[Install]
WantedBy=multi-user.target

And that's it! Here it is all put together:

[Unit]
Description=My Nextcloud
After=mariadbnextcloud.container redisnextcloud.container

[Container]
Image=lscr.io/linuxserver/nextcloud:latest
Network=pasta
Environment=PUID=1000
Environment=GUID=1000
Environment=America/Detroit
Environment=REDIS_HOST=localhost
Environment=REDIS_PORT=6379
Volume=nextcloud_data:/config
Volume=/evimedia/nextcloud:/evimedia/nextcloud
PublishPort=127.0.0.1:6443:443

[Service]
Restart=always

[Volume]
VolumeName=nextcloud_data

[Install]
WantedBy=multi-user.target

All that's left is to pull your image, then run the following to start the container. Note that enabling the service is not necessary when using containers.

podman pull lscr.io/linuxserver/nextcloud:latest
systemctl --user daemon-reload
systemctl --user start nextcloud.container

And that's it! If you run "podman ps", you should see the name of your container as "systemd-<container name>".

There's a lot more you can do with Podman Quadlets (even more than just containers!). If you liked this write up, stay tuned, because I never shut up about Podman.