CertGrid CertGrid
Concepts·Podman

podman-compose and Docker Compose Differences

The same compose.yaml gives you `stack_web_1` from podman-compose and `stack-web-1` from `podman compose`. Underscores against hyphens, two different programs, and any script naming a container breaks when you switch.

Registries and Remote Hosts Guide 37 of 47 Intermediate

Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. Three commands that all look like Compose

    There are genuinely three things here and the names are unhelpfully similar:

    • podman-compose - a hyphenated Python program. It reads your compose file and issues podman commands. No socket involved.
    • docker compose - the real Compose v2 from docker-compose-v2, talking to a Docker API. That is the previous guide.
    • podman compose - a *space*, not a hyphen. A thin wrapper that finds whichever external provider you have and runs it.

    podman-compose version reports both podman's version and its own, which is a fair summary of what it is - a layer on top of the CLI.

    podman compose version announces the delegation explicitly:

    >>>> Executing external compose provider
    "/usr/libexec/docker/cli-plugins/docker-compose". Please see
    podman-compose(1) for how to disable this message. <<<<

    Podman is telling you it is not implementing Compose - it found Docker's binary and ran it. Note this notice is separate from the Emulate Docker CLI one in guide 4; silencing that one with /etc/containers/nodocker leaves this one in place.

    bash Example session
    command -v podman-compose docker-compose/usr/bin/podman-composepodman-compose versionpodman version 5.7.0podman-compose version 1.5.0docker compose version>>>> Executing external compose provider "/usr/libexec/docker/cli-plugins/docker-compose". Please see podman-compose(1) for how to disable this message. <<<< Docker Compose version 2.40.3+ds1-0ubuntu1

    Expected resultpodman-compose on the PATH, its own version output, and the external provider notice.

    Success conditionYou can tell the three commands apart by name.

  2. The same file, two sets of names

    Run podman-compose up -d on the file from the previous guide, then look at the container names:

    stack_web_1    docker.io/library/nginx:alpine
    stack_cache_1  docker.io/library/redis:alpine

    Underscores, and a _1 suffix - the Compose v1 convention.

    Now the provider route:

    stack-web-1    docker.io/library/nginx:alpine
    stack-cache-1  docker.io/library/redis:alpine

    Hyphens - the v2 convention.

    One compose file, two tools, two different container names. Anything that names a container breaks when you switch: a podman exec in a deploy script, a log collector's filter, a monitoring check, a colleague's runbook. And because both tools work, nothing warns you.

    Pick one per project and write it down. That is the practical advice, and it matters more than which one you pick.

    bash Example session
    cd ~/stack && podman-compose up -dfc2494c69ce989ae8e96274225e18ecc27c0e260fcaffc0a8fa285dd1aa99882acf87b7385a56ecd5c15cc531752bee103a5632c4e1f063c0cd180ea845b03e9e7cbb2528c03f3b123d177865ae4d04544ca88857d90cbbe5d2897bfaba1f0e0stack_web_1stack_cache_1podman ps --format 'table {{.Names}} {{.Image}}'NAMES          IMAGEstack_web_1    docker.io/library/nginx:alpinestack_cache_1  docker.io/library/redis:alpinecd ~/stack && podman-compose downstack_cache_1Error: removing container acf87b7385a56ecd5c15cc531752bee103a5632c4e1f063c0cd180ea845b03e9 network: 1 error occurred:	* rootless netns: kill network process: permission denied  stack_cache_1stack_web_1fc2494c69ce989ae8e96274225e18ecc27c0e260fcaffc0a8fa285dd1aa99882cd ~/stack && podman compose up -d>>>> Executing external compose provider "/usr/libexec/docker/cli-plugins/docker-compose". Please see podman-compose(1) for how to disable this message. <<<<  Network stack_default  Creating Network stack_default  Created Container stack-web-1  Creating Container stack-cache-1  Creating

    Expected resultUnderscore names from podman-compose and hyphen names from the provider.

    Success conditionYou have produced two different container names from one file.

  3. Which to use

    The external provider (docker compose or podman compose) for anything real. It is the reference implementation, it tracks the Compose specification, and features land there first. If your compose file uses depends_on conditions, healthchecks, profiles or watch, this is the one that implements them properly.

    podman-compose when you want no socket. Because it drives the CLI rather than an API, it needs nothing enabled - which suits a locked-down host, or a case where you would rather not have a Docker-compatible API listening at all. It is also pure Python and easy to read when you want to know exactly what a compose file will do.

    Its trade-off is coverage: it implements a large subset of the spec, and the gaps are in the newer features. When a compose file behaves oddly under it, check against the provider before debugging your file.

    Worth noting both hit the rootless netns error on teardown - podman-compose down prints it too. It is the environment, not the tool, and guide 23 is where that is pinned down.

    And the honest recommendation for anything unattended: neither. Compose is a development tool. A Quadlet unit (guide 40) gives you systemd's restart handling, dependency ordering and boot integration, which is what a long-running service actually needs.

    bash Example session
    cd ~/stack && podman compose down>>>> Executing external compose provider "/usr/libexec/docker/cli-plugins/docker-compose". Please see podman-compose(1) for how to disable this message. <<<<  Container stack-cache-1  Stopping Container stack-web-1  Stopping Container stack-web-1  Stopped Container stack-web-1  Removing Container stack-web-1  Removed Container stack-cache-1  Error while StoppingError response from daemon: removing container 0b1167ae05901e21ec9446e539342194a76e16a8ea7bbb5a6f1129f0fa0f231d network: 1 error occurred:	* rootless netns: kill network process: permission deniedpodman ps -a --format 'table {{.Names}} {{.Status}}'NAMES          STATUSstack-cache-1  Exited (0) Less than a second ago

    Expected resultThe stack torn down.

    Success conditionYou can choose between the two and say what you gain either way.

Troubleshooting

Official sources