A missing blog post image

Introduction

Docker Compose has been a regular way of distributing/running container stacks to deploy services for many years now. What is actually interesting is the “Compose spec”, which mainly defines static representation(s) (it used to be multiple versions of it) of these stacks (i.e. how different services share resources [or not], and “work” together).

For this second post of this “Podman series” (also see “Podman rootless in Podman rootless, the Debian way”), I will explain how I replaced Docker-Compose (and hence Docker itself) by Podman-Compose for some personal services.

From Docker-Compose to Docker Compose

Historically, Composing with Docker was achieved by using Docker-Compose (v1), a binary which was actually a Python program built using PyInstaller, and invoked by docker-compose. On Debian, it is still packaged in main repositories.

Since 2021 (and mostly since last year), Composing is now achieved using Docker Compose (v2), a Go plugin for the docker (client) CLI.
On Debian, once Docker repository has been added, setup is pretty straightforward : apt install docker-ce docker-compose-plugin.
Invocation is then done by docker compose (please note the space instead of the dash).

In either case, a docker-compose.yml file is usually downloaded according to vendor recommendations (or carefully handcrafted when missing :stuck_out_tongue:), and modulo a docker[-]compose up -d invocation, you’re all set and running.

Composing with Podman

Whereas Podman has been built as a “drop-in replacement” for Docker (despite internals being fundamentally different), Composing was initially out-of-scope (or very partially supported). But it’s 2023, and Podman-Compose is now a robust alternative.

Within this containers ecosystem where (almost) everything is written in Go, please welcome Python again (and mind the coming back of the dash !).

Podman-Compose implements the Compose spec, while still being oriented “rootless” (after all, it’s what we expect from Podman, isn’t it ?) and “daemon-less”.

Podman-Compose setup for Debian 12 (Bookworm)

First we create a system user dedicated to Podman execution :

adduser --system namdop --group --home /opt/namdop

Unlike Docker, there is no such thing as a (privileged) daemon running and waiting for us to give commands.
So we must enable systemd-logind “lingering” for our system user to make systemd start (in user mode) on machine boot, and thus provide a proper runtime for OOTB Podman execution :

loginctl enable-linger namdop

Now we can install Podman and deal with Debian (undesired) pulled dependencies :

apt-get install -y --no-install-recommends \
    podman \
    slirp4netns \
    uidmap \
    golang-github-containernetworking-plugin-dnsname

systemctl --global disable --now \
    dirmngr.socket \
    gpg-agent.socket \
    gpg-agent-browser.socket \
    gpg-agent-extra.socket \
    gpg-agent-ssh.socket

We then define our ranges of UIDs/GIDs dedicated to our system user :

echo "namdop:100000:65536" > /etc/subuid
echo "namdop:100000:65536" > /etc/subgid

:warning: Podman-Compose is packaged in Debian main repositories, but (already) pretty old so you’ll understand why we go through PyPI to retrieve an up-to-date version.

As we like doing clean things and we actually don’t wanna break our system, we will install podman-compose in a Python virtual environment :

apt-get install -y python3-venv

Now we can finalize the setup as our regular system user :

su - namdop -s /bin/bash

python3 -m venv venv && source venv/bin/activate
pip3 install -U pip wheel

pip3 install podman-compose

mkdir my_service && cd my_service/

# Here you can setup your docker-compose.yml and required configuration/data, as you would do with Docker !
# ...

podman-compose up -d

The restart policy pitfall

Sooo, as you may already know, containers do not start on boot by default. This behavior depends on the --restart={always,no,on-failure[:max-retries],unless-stopped} flag, that you can should specify when creating a container (or through the restart key in a Compose file).

Unlike for “standalone” Podman where we could run podman generate systemd <container> to make a container actually managed by systemd as a regular “service” (and thus enjoy the Restart policy feature), there is no such thing (yet ?).

As a workaround, we can rely on the (global) systemd service unit (called podman-restart.service), responsible for restarting containers on boot, that we must enable in our (user mode) systemd runtime :

XDG_RUNTIME_DIR="/run/user/$(id -u)" systemctl --user enable --now podman-restart.service

Whereas unless-stopped is supposed to be identical to always with Podman, packaging hits us hard and that’s unfortunate.
podman-restart.service unit specifies --filter=always to only (re)start containers that should be.

So you must add :

services:
  service:
    # ...
    restart: always
    # ...

… to your Compose stacks to enjoy containers auto-restart, or you will have to tweak the upstream systemd unit :clown_face:

This issue has already been mentioned two years ago by @plevart, but it has not been fixed yet.

Conclusion

apt-get autoremove --purge docker-ce docker-compose-plugin

rm -f /etc/apt/keyrings/docker.gpg
rm -f /etc/apt/sources.list.d/docker.list
apt-get update

A missing blog post image