Running existing docker-compose.yml files on top of Podman with podman-compose: understanding the manifest anatomy, Docker API compatibility, Podman Desktop integration as a GUI, and opening the Docker API socket via podman system service so docker-compatible tooling keeps working.

In episode 11 you tidied up Podman configuration and storage: containers.conf, registries.conf, storage.conf, and an understanding of the overlayfs layout for both rootless and rootful containers. Now it's time to level up from a single container to many containers at once. Episode 12 opens the orchestration phase: running the docker-compose.yml files you already have on top of Podman, using Podman Desktop as a GUI, and exposing the Docker API socket so the Docker tooling ecosystem stays compatible.
Podman does solve the daemon problem, but defining dozens of containers one by one with podman run commands is impractical. Compose answers the need to declare an entire application — web, API, database, cache, worker — in a single manifest that can be reviewed, version-controlled, and reproduced in other environments.
On top of Podman there are two main paths to using the compose concept:
docker-compose.yml files and translates them into Podman commands.podman system service provides a Docker-compatible API, so tooling like docker compose and docker-py can be pointed at Podman.Both make the move from Docker to Podman feel almost frictionless.
A compose manifest is a YAML document with several top-level sections. An example three-service application:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
depends_on:
- api
api:
build: ./api
environment:
DB_HOST: postgres
secrets:
- db_password
postgres:
image: docker.io/library/postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
restart: unless-stopped
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
pgdata:The services section defines containers; volumes, networks, and secrets are shared resources referenced from services. This is what declarative means: you state what you want, not how to run it.
| Compose element | Podman equivalent |
|---|---|
services | podman run or a Quadlet .container unit |
volumes | podman volume create |
networks | podman network create |
ports | the -p flag on podman run |
environment | the -e flag or --env-file |
restart | the --restart flag |
depends_on | start order handled by podman-compose |
depends_on doesn't mean "wait until the database is ready", only "start after the database has started". Healthchecks remain your responsibility at the application level.
Install podman-compose, then run it in the directory containing docker-compose.yml:
podman-compose up -d
podman-compose ps
podman-compose logs -f api
podman-compose downpodman-compose up -d reads the manifest and creates the required networks, volumes, and containers. Because it's translated into ordinary Podman commands, the resulting containers aren't managed by a daemon — you can inspect them directly with podman ps and podman inspect.
Tip
podman-compose isn't a daemon and doesn't lock the manifest. If a service needs to run manually with special flags, you can still podman run that service and let podman-compose manage the rest — there's no double-booking like on daemon-based engines.
The term "compatible" needs to be understood precisely. podman-compose reads the same docker-compose.yml file format, but runs it through Podman's mechanics. That means:
For most stacks — web plus database plus cache — this compatibility is more than enough.
Podman Desktop is the official GUI from the Podman project. You can manage containers, pods, images, volumes, all the way to Kubernetes deployments without typing commands. For multi-container composition, Podman Desktop reads docker-compose.yml and provides a graphical view to run and monitor the stack. It's a convenient choice when onboarding team members who aren't yet familiar with the CLI.
Beyond podman-compose, Podman can speak as a Docker Engine through a compatible API endpoint. The key command:
podman system service --time=0 unix:///tmp/podman.sockWith --time=0 the service never times out. Now DOCKER_HOST can be pointed at that socket:
export DOCKER_HOST=unix:///tmp/podman.sock
docker compose up -dImportant
By default podman system service runs in the foreground and waits on a unix socket. For production, run it as a systemd unit and restrict socket access to trusted users — this socket has the power to run containers on your behalf.
Because the socket above serves an API compatible with Docker Engine, much of the tooling long used with Docker works directly without modification:
docker ps, docker build, docker exec.docker compose subcommand.This practice is what earned Podman the nickname of a drop-in Docker replacement: at the protocol level, clients can't tell the difference.
In episode 12 you ran a multi-container stack on top of Podman: reading and understanding the anatomy of docker-compose.yml, translating it with podman-compose, getting to know Podman Desktop as a GUI, opening the Docker API socket with podman system service --time=0, and using docker-compatible tooling via DOCKER_HOST.
The key points to take home:
docker-compose.yml file can be run by both Docker and Podman.podman.podman system service --time=0 opens the Docker API gateway for the entire tooling ecosystem.In the next episode, Episode 13, we enter the security phase: security model — how user namespaces, SELinux, AppArmor, capabilities, and seccomp work together to secure your containers. This is the layer that determines how far you're willing to run untrusted workloads.