CertGrid CertGrid
Concepts·Podman

Podman and Docker CLI Differences

`podman-docker` puts a /usr/bin/docker on your PATH and most commands just work. Then `docker info --format '{{.ServerVersion}}'` exits 125, because there is no server and the field does not exist.

Foundations Guide 4 of 47 Beginner

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. Install the shim

    There is no docker command on this host. podman-docker provides one.

    Note what it drags in: docker-compose-v2, as a dependency. That matters in step 4, and it is not the same thing as Podman being able to run a Compose file.

    bash Example session
    command -v docker [exit 1]sudo -n apt-get install -y podman-dockerReading package lists...Building dependency tree...Reading state information...Solving dependencies...The following additional packages will be installed:  docker-compose-v2Recommended packages:  docker.ioThe following NEW packages will be installed:  docker-compose-v2 podman-dockerdebconf: unable to initialize frontend: Dialogdebconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.)

    Expected resultNothing from command -v, then two new packages installed.

    Success conditionpodman-docker and docker-compose-v2 are installed.

  2. What the shim actually is

    It is worth reading, because it is three lines long and it explains the entire compatibility story.

    /usr/bin/docker is not a binary and not a symlink. It is a shell script that prints a notice and then executes podman with the arguments it was given. There is no translation layer, no argument rewriting, and no daemon shim - the reason docker run works is simply that podman run accepts the same flags.

    The notice on every invocation is deliberate. Create /etc/containers/nodocker to silence it, which you would do on a build agent and probably not on a workstation where the reminder is the point.

    bash Example session
    command -v docker [exit 1]file /usr/bin/docker/usr/bin/docker: POSIX shell script, ASCII text executablecat /usr/bin/docker#!/bin/sh[ -e /etc/containers/nodocker ] || [ -e "${XDG_CONFIG_HOME-$HOME/.config}/containers/nodocker" ] || \echo "Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg." >&2exec /usr/bin/podman "$@"

    Expected resultA three-line /bin/sh script ending in exec /usr/bin/podman "$@".

    Success conditionYou have read the compatibility layer end to end.

  3. Most things simply work

    docker --version reports podman version 5.7.0, which is the shim being honest rather than pretending.

    docker ps and docker run behave exactly as the Podman commands do, because they are the Podman commands. The emulation notice prints first, then the real output follows.

    For day-to-day work - run, ps, build, pull, exec, logs, rm - the shim is genuinely enough, and a Makefile or a CI script full of docker invocations will usually run unmodified.

    bash Example session
    docker --versionEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.podman version 5.7.0docker psEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMESdocker run --rm docker.io/library/hello-worldEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. Hello from Docker!This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.    (amd64) 3. The Docker daemon created a new container from that image which runs the    executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it    to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/

    Expected resultpodman version 5.7.0, an empty container list, and the hello-world greeting.

    Success conditionDocker commands are running containers through Podman.

  4. Where it stops

    Now the failure that defines the limit:

    $ docker info --format '{{.ServerVersion}}'
    Error: template: info:1:9: executing "info" at ...
    can't evaluate field ServerVersion in type system.infoReport

    Exit 125. Docker's info has a ServerVersion because Docker has a server. Podman's info returns a different structure entirely, with no such field, because there is no server. The shim forwards your arguments faithfully and Podman rejects them honestly.

    That is the rule for the whole compatibility story: the shim renames the command, it does not reshape the output. Anything that parses docker output - a monitoring agent, a script reading docker info, a tool expecting Docker's JSON schema - is where migration work actually lives.

    docker compose is the other half. It runs, because docker-compose-v2 came in as a dependency, and it announces itself as an external compose provider. It is the real Compose binary, and to reach containers it needs a Docker-compatible API socket that nothing has enabled yet. That is the Compose and Remote Hosts track.

    bash Example session
    docker info --format 'server={{.ServerVersion}}'Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.server=Error: template: info:1:9: executing "info" at <.ServerVersion>: can't evaluate field ServerVersion in type system.infoReport[exit 125]docker compose versionEmulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.>>>> 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-0ubuntu1ls -l /etc/containers/nodockerls: cannot access '/etc/containers/nodocker': No such file or directory[exit 2]

    Expected resultExit 125 on the template, a Compose version with an external-provider notice, and no nodocker file.

    Success conditionYou can name a docker command the shim cannot satisfy, and say why.

Troubleshooting

Official sources