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 ), 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
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
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