Podman – Quadlets – The Basics

Last modified date

Comments: 0

Having been playing with Docker recently and ended up down the root/rootless container rabbit hole, I then stumbled across Podman.

Quadlets

Whilst rootless Podman can work with your existing docker-compose.yml file, v4.4 (I believe) and newer allow you to write Quadlet files which are turned into systemd services. You can then stop / start containers using systemctl rather than docker or podman commands eg:

systemctl --user start <container service name>

So rather than use or try to convert an existing docker-compose.yml, I thought I would start from scratch with Quadlets.

Podman Installation

I’ve been testing this on Ubuntu 24 LTS which lags the most recent releases of Podman. Running

sudo apt install podman

installed v4.9, whereas the most recent release is (as I type this) v5.7.1. A non-LTS Ubuntu image (eg 25) should get v5.3 or newer. Alternatively, other Linux distributions will have 5.x in their repo’s so if you want the latest features before Ubuntu 26 LTS arrives, Ubuntu LTS is probably not the best choice.

Once installed, you can check the installed version via:

podman --version

At this point I created another user called podman to use. All the containers will run under this user. This user will not have the ability to sudo, so the containers will be rootless. Creating the new user is done under my usual login which can sudo:

sudo useradd -m -s /bin/bash podman
sudo passwd podman

To ensure containers can run when our podman user isn’t logged in (eg after a reboot), we need to enable linger:

sudo loginctl enable-linger podman

The guides I found (useful one here) required you to edit the /etc/subuid and /etc/subgid files and adding an extra line for your new user. However, I found these already created. To check:

cat /etc/subuid
chris:100000:65536
podman:165536:65536
cat /etc/subgid
chris:100000:65536
podman:165536:65536

My (admittedly limited!) understanding is that the ID values shouldn’t overlap, so the UID for chris starts at 100000 and provides 65536 IDs (so ends as 165535). The podman user than has the same ammount of space starting at 165536. You can edit these files as sudo to add the extra mapping if required.

If you want a Podman container to use a privileged port, then edit /etc/sysctl.conf and add the line to allow ports upwards from 80 to be used:

net.ipv4.ip_unprivileged_port_start=80

Getting Started

This example will launch a Nginx container.

At this point we should login as our new user, podman.

Podman will look in a few places for the Quadlet configuration files depending if you are root or rootless (see here). I’ve gone with the option to store these inside the Podman user home folder, so the path to create is:

mkdir -p $HOME/.config/containers/systemd

We can create a simple network configuration to use with our container. I’m going to call the network Skynet.

nano .config/containers/systemd/skynet.network

Into this file we can put the following configuration.

[Unit]
Description=Skynet
After=network-online.target

[Network]
NetworkName=skynet

[Install]
WantedBy=default.target

To explain some of the above:

  • After=network-online.target ensures the service waits for the host network at startup
  • WantedBy=default.target sets the service to start at bootup

If we wanted to, we could specify the IP subnet to use with appropriate gateway, DNS and other settings in the [Network] section but we don’t have to. When left blank, Podman will create them for us and use a private subnet.

Now we can create the Quadlet for our Nginx container:

nano .config/containers/systemd/nginx.container

The contents being:

[Unit]
Description=Nginx Webserver

[Container]
ContainerName=Nginx
Image=docker.io/nginx
Network=skynet
PublishPort=80:80

[Service]
Restart=on-failure

[Install]
WantedBy=default.target

Notice how we are using an image from the docker.io repository – Podman uses the same standards (Open Container Initiative) as Docker, so the same container image can be used on both platforms. We haven’t specified a version of the Nginx container to use, so it will default to latest. To specify a version, add the version you need to the end of the Image line eg “nginx:1.29.4”. The container will be part of our Skynet network and port 80 will be published via the hosting VM.

At this point we can reload systemd to pick up the configuration files, then start the network and container.

systemctl --user daemon-reload

All being well, you’ll see no response from reloading. Now to start the services.

systemctl --user start skynet

Gotcha alert! I got this far in my initial testing and spent an age trying to figure out why when trying to start the network, I kept getting told “Failed to start skynet.service: Unit skynet.service not found.” I was Googling, looking in logfiles – all the usual stuff trying to find a clue. Then I realised how your Quadlets are converted into services.

  • nginx.container becomes nginx.service (or just nginx)
  • skynet.network becomes skynet-network.service (or just skynet-network)

Yes, I was trying to start a service that didn’t exist. So, the correct commands are:

systemctl --user start skynet-network
systemctl --user start nginx

On the first run, the nginx service might be slow starting as it will pull the image from the docker.io repo’, so useful commands to see what is happening are:

systemctl --user status skynet-network
systemctl --user status nginx
podman ps 

As an example, here is podman ps showing everything looking good:

Now to test Nginx using Curl:

curl http://0.0.0.0:80

Curl should return the raw HTML for the standard Nginx “Welcome to Nginx” page . Alternatively, a web browser pointed at the IP of your Podman host VM should also show the Nginx page (firewalls etc allowing).

That should be a very simple test but gives a foundation for building more advanced configurations include mapping host volumes into a container, multiple container environments and the like.

Making Changes

If you edit your config files (.eg container), you need to execute systemctl –user daemon-reload before trying to restart the service (systemctl –user restart <service-name>) otherwise you will see the message:

Podman Command Reference

CommandOperation
podman execRun a command on the container. For instance, to run a shell you might use:
podman exec -it <container_name> /bin/bash
podman imagesList local container images
podman logs <container_name>
podman logs <container_id>
View the logs for the specified container. Container name is case-sensitive. Obtain the Container ID using podman ps
podman psList details of the running containers

Chris

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.