Podman Quadlets, How To Run Your Containers As Services!

Podman quadlets is an awesome feature that allows you to write a simple service file that podman uses to generate systemd .service files for you. This allows systemd to do the heavy lifting of running our container as a service with all the bells and whistles of a normal systemd service.

If you are new to podman, here is how you get started ;)

Where we define our podman quadlet files

To get started with podman quadlets, we first need to know where to define our quadlet config files:

Rootful quadlet config directories

For rootful quadlet containers we can define them in one of the following directories with order of precedence (/run is prioritized over /etc):

/run/containers/systemd/ # temporary testing quadlets
/etc/containers/systemd/ # sys admin quadlets
/usr/share/containers/systemd # distribution defined quadlets ?

For non test quadlets, I recommend /etc/containers/systemd/ as to be honest, I have no idea what the distribution defined quadlets are. When I figure it out, I'll update this section.

Rootless quadlet config directories

Our rootless quadlets can be defined in the following directories:

$XDG_RUNTIME_DIR/containers/systemd/
$XDG_CONFIG_HOME/containers/systemd/
~/.config/containers/systemd/
/etc/containers/systemd/users/$(UID)
/etc/containers/systemd/users/

I recommend ~/.config/containers/systemd/ for rootless users as it's in their home directory, but it's up to you ;)

Let's write those Podman Quadlet files

Now that you've picked a directory for your rootful / rootless quadlet. I'll be going with rootless. Let's take a look at the service file

Types of Quadlet config files

  • .container - define a podman quadlet container service file
  • .kube - define a kube file to be generated as a systemd service, managing the pod and all the containers based off of a kubernetes yaml file that podman can generate for you.
  • .volume - automatically setup volumes for use by other containers.
  • .network - automatically setup networks for use by your container services.

The .container Quadlet config file

To define a container as a service, we create .container file.

The name of your systemd service managing your container will be the name of the file. For example a container service named jazedev will have a jazedev.container file.

I don't show everything there is to know about the .container file. If you want to look at what options you have, I highly recommend reading the podman quadlet documentation here.

Let's have a look at what the .container file contents will look like and the option you'll probably use the most:

[Unit]
# systemd service options:
Description=your service description

# 'on-failure' will restart the service only if it exits with an error
# options: always | on-failure | no | unless-stopped
Restart=on-failure
RestartSec=5 # delay in seconds before trying to restart

# if you want to start after another container has started:
After=name.container

# if you need a specific container to be running to start
Requires=name.container

[Container]
# podman common container options:
ContainerName=name # name of your container like the --name flag, will default to systemd-name
Image=image:tag # prefers full paths like docker.io/postgres:latest, but short ones do work
PublishPort=8080:3000 # host to container port mapping like the -p flag

Network=YourNetwork # podman network to assign container to
Network=yournetwork.network # quadlet network file

Pod=MyPodName # if you want your container to be a part of a pod.
Pod=mypod.pod # quadlet pod file

# mount volumes, similiar to the -v flag in podman run
Volume=named-volume:/container/path:z
Volume=/host/path:/container/path
Volume=myvolume.volume # quadlet volume

# specify env vars like -e flag in podman run:
Environment=MY_ENV=Something

# if you want to load an env file instead:
EnvironmentFile=/path/to/file.env

[Install]
WantedBy=default.target # start container service on start up

Starting your containers

Now, once you have defined your quadlet files and you're ready to run your containers as services, the next steps are pretty simple.

Gotcha on Ubuntu 22.04

I'm not too sure what the issue with podman quadlets on this ubuntu version, but the linking between the systemd-generator that is used doesn't get setup correctly.

This means when we run systemd --user daemon-reload. Quadlets doesn't pickup our changes and create service files for systemd.

To get around this issue, there are two solutions, install podman from official upstream repo (you get the latest version), I'm going to write a guide on how to do that as I want the latest version.

OR invoke this command /usr/libexec/podman/quadlet --user "$XDG_RUNTIME_DIR/systemd/user" after any changes to your quadlet files before running the systemd reload command.

You'll need to invoke that quadlet command at startup as well. We can automate this with systemd:

Create a new service file here: ~/.config/systemd/user/quadlet-generate.service and paste this in:

[Unit]
Description=Run Podman Quadlet generator to create container services
After=default.target

[Service]
Type=oneshot
ExecStart= /bin/sh -c '/usr/libexec/podman/quadlet --user "$XDG_RUNTIME_DIR/systemd/user"'

[Install]
WantedBy=default.target

Once you've done that, we just need to reload the service, enable and then start the service and forget about it.

systemctl --user daemon-reload
systemctl --user enable quadlet-generate
systemctl --user start quadlet-generate

How to start your container services

To start our containers as services we will be working with systemd and will need to execute these commands in this order:

NOTE: After any changes to your quadlet files, you will need to daemon-reload and restart your service.

# if your running rootful, include sudo, otherwise include the --user flag for all systemctl commands.

# work around for podman v4.9 on ubuntu v22.04
/usr/libexec/podman/quadlet --user "$XDG_RUNTIME_DIR/systemd/user"

# reload the daemon thingy to pickup service file changes from quadlets (and workaround)
systemctl {--user} daemon-reload

# enable your new container service so that it starts at startup
# your service name will be the same name as the name property you specified in your .container file
# or the name of the .container file if you didn't specify a name in the file.
systemctl {--user} enable yourservice

# start your container as a service!
systemctl {--user} start yourservice

# check the status of your container as a service to make sure its running okay
# if not make use the journalctl command it will give you for further debugging.
systemctl {--user} status yourservice

Done! Your container is now running as a service!

Wrapping Up

Podman quadlets is a fantastic feature and I love it, hopefully this guide did it justice by showing you how to use it.

Definitely one of my favourite podman features by far and you can combine it with kube to have 1 quadlet file to start up your whole container setup. Get creative, the more the better ;)

As always, thanks for reading and I hope this helped you on your baby seal journey!

As a solo dev writing guides in my spare time, if this guide helped you, please: