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
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- TimeAbout 12 min
- Reviewed22 August 2026
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.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
- Podman installed - see guide 1.
- No Docker Engine on the host. If one is installed,
podman-dockerwill refuse to overwrite its/usr/bin/docker.
-
Install the shim
There is no
dockercommand on this host.podman-dockerprovides 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 condition
podman-dockeranddocker-compose-v2are installed. -
What the shim actually is
It is worth reading, because it is three lines long and it explains the entire compatibility story.
/usr/bin/dockeris not a binary and not a symlink. It is a shell script that prints a notice and then executespodmanwith the arguments it was given. There is no translation layer, no argument rewriting, and no daemon shim - the reasondocker runworks is simply thatpodman runaccepts the same flags.The notice on every invocation is deliberate. Create
/etc/containers/nodockerto 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/shscript ending inexec /usr/bin/podman "$@".Success conditionYou have read the compatibility layer end to end.
-
Most things simply work
docker --versionreports podman version 5.7.0, which is the shim being honest rather than pretending.docker psanddocker runbehave 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
dockerinvocations 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 result
podman version 5.7.0, an empty container list, and the hello-world greeting.Success conditionDocker commands are running containers through Podman.
-
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.infoReportExit 125. Docker's
infohas aServerVersionbecause Docker has a server. Podman'sinforeturns 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
dockeroutput - a monitoring agent, a script readingdocker info, a tool expecting Docker's JSON schema - is where migration work actually lives.docker composeis the other half. It runs, becausedocker-compose-v2came 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
nodockerfile.Success conditionYou can name a
dockercommand the shim cannot satisfy, and say why.
Troubleshooting
dpkg: error processing archive podman-docker: trying to overwrite '/usr/bin/docker', which is also in package docker-ce-cli.Why: Docker Engine is installed. Two packages cannot own the same path.
Fix:Decide which one owns
dockeron this host. Removedocker-ce-cli, or skip the shim and usepodmandirectly - an alias in your shell does the same job without a package conflict.The
Emulate Docker CLI using podmannotice breaks a script that parses output.Why: It is printed on every invocation, to stderr.
Fix:
sudo touch /etc/containers/nodocker. That is the documented way to silence it, and it is what the message itself tells you.docker compose upfails withCannot connect to the Docker daemon at unix:///var/run/docker.sock.Why: Compose is the real Docker Compose and speaks the Docker API. Nothing is serving that API.
Fix:Enable Podman's socket and point Compose at it -
systemctl --user enable --now podman.socket, thenexport DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock. Covered properly in the Compose and Remote Hosts track.A tool reports a Docker API version it does not support.
Why: Podman's Docker-compatible API implements a specific API version, and some clients pin to a newer one.
Fix:
curl --unix-socket $XDG_RUNTIME_DIR/podman/podman.sock http://d/v1.41/versionshows what is served. Most clients accept an override through their own configuration.