What "drop-in" covers, and where it stops
The claim comes from Podman's own introduction: it "provides a command line interface (CLI) familiar to anyone who has used the Docker Container Engine" and "most users can simply alias Docker to Podman (alias docker=podman) without any problems". Read that as a statement about the command surface. Images are OCI images either way, a Containerfile is a Dockerfile, registries and login flows are the same, and run, build, pull, push, exec, logs and ps behave the way your fingers expect.
Below the CLI the two engines part ways, and that is what a migration has to check. The split below is the honest version of the alias; the rest of this page is the evidence for each line.
Process model and what runs as root
Docker's security page states the model plainly: "running containers (and applications) with Docker implies running the Docker daemon. This daemon requires root privileges unless you opt-in to Rootless mode", and "only trusted users should be allowed to control your Docker daemon" because the daemon will happily bind-mount the host's / into a container. The CLI talks to that daemon over a Unix socket, and access to the socket is access to the daemon. Rootless mode "executes the Docker daemon and containers inside a user namespace", so both run without root, using no setuid binaries "except newuidmap and newgidmap". The hands-on setup of both rootless modes, and what a container escape can still reach, is worked through in the Rootless Docker and Podman lesson.
Podman has no daemon to trust. It "relies on an OCI compliant Container Runtime (runc, crun, runv, etc)" and its containers "can either be run by root or by a non-privileged user". Each podman command sets up the container, hands it to conmon to keep it alive and collect its exit status, and returns. There is no privileged process holding every container, which is the property people are buying, and there is no process to ask for state either, which is the property they sometimes miss: podman ps reads the container store, it does not query a service.
docker infoClient: Docker Engine - Community Context: rootlessServer: Security Options: seccomp Profile: builtin rootless cgroupnspodman infohost: networkBackend: netavark ociRuntime: name: crun remoteSocket: path: /run/user/1000/podman/podman.sock security: rootless: truestore: graphDriverName: overlay graphRoot: /home/alice/.local/share/containers/storageThose two excerpts already contain most of the migration checklist. Docker tells you which context you are in and lists rootless under Security Options; Podman tells you the runtime, the network backend, where the socket would be if you started one, and that the image store lives under the user's home. If a script assumes /var/lib/docker or /var/run/docker.sock, it will read those lines and fail.
Rootless on real servers: prerequisites and documented limits
The prerequisites are nearly identical, which is unsurprising since both use the same kernel features. Docker requires newuidmap and newgidmap and says /etc/subuid and /etc/subgid "should contain at least 65,536 subordinate UIDs/GIDs for the user"; Podman needs the same files, and its rootless notes state that "a standard rootless configuration only gives containers access to 65536 UIDs and GIDs". Docker's setup is a script, dockerd-rootless-setuptool.sh install, that creates a user-level docker.service and a "rootless" CLI context. Podman needs no install step beyond the packages: run it as the user and it is rootless.
The limits are where the two diverge, and both projects document them. Rootless Docker supports only overlay2 (kernel 5.11 or later), fuse-overlayfs, btrfs and vfs as storage drivers; cgroup resource limits work "only when running with cgroup v2 and systemd"; AppArmor, checkpoint, overlay networks and SCTP ports are "not supported"; privileged ports below 1024 need extra host configuration; and networking runs through a user-mode TCP/IP stack that "is generally slower than the one in kernel mode". Rootless Podman uses native overlayfs on kernel 5.12 or later and falls back to fuse-overlayfs; checkpoint and restore do not work because "CRIU requires root"; resource limits are unavailable on cgroups v1; ports below 1024 need the same net.ipv4.ip_unprivileged_port_start sysctl; and images that need UIDs beyond the 65,536 mapped entries fail to build or run.
Networking is where the defaults differ in kind. "As of Podman 5.0, pasta is the default networking tool" for rootless Podman. Rootless Docker uses slirp4netns when it is installed and gvisor-tap-vsock otherwise (pasta is documented as experimental and VPNKit as legacy), and since Engine 29.5 --net=host is no longer namespaced inside RootlessKit. Either way, rootless container networking is a user-space stack, and throughput-sensitive services should be measured before you commit a fleet to it.
Boot, restart and the socket: who supervises your containers
Docker supervises. "Docker provides restart policies to control whether your containers start automatically when they exit, or when Docker restarts", and the docs recommend them over process managers with a direct warning: "Don't combine Docker restart policies with host-level process managers, as this creates conflicts." The daemon is the supervisor, which also means that when the daemon stops, so do the containers, unless live-restore is enabled in daemon.json; and live restore "is only supported when installing patch releases", not across major daemon upgrades.
Podman delegates to systemd. Quadlet is "a systemd generator": you drop a .container file into /etc/containers/systemd/ (rootful) or ~/.config/containers/systemd/ (rootless), systemd reads it at boot and on daemon-reload, and generates a normal .service unit you manage with systemctl. The older podman generate systemd is "deprecated" with "no plans to remove the command", receiving urgent fixes only. Restart=always, dependencies, timers and journald logging are systemd's, not Podman's.
# Docker: the daemon owns the restartdocker run -d --name web --restart unless-stopped \-p 8080:80 -v web-data:/data registry.example.com/shop/web:2.4.1# Podman: ~/.config/containers/systemd/web.container (rootless) or# /etc/containers/systemd/web.container (rootful)[Unit]Description=shop webAfter=network-online.target[Container]Image=registry.example.com/shop/web:2.4.1PublishPort=8080:80Volume=web-data:/dataAutoUpdate=registry[Service]Restart=alwaysTimeoutStartSec=900[Install]WantedBy=default.target# then: systemctl --user daemon-reload && systemctl --user start web# Docker-API clients: systemctl --user start podman.socket# export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
The socket follows the same split. Docker's socket is always there because the daemon is always there. Podman's API "supports systemd socket activation": podman.socket listens, podman system service starts on the first connection and, "after some time of inactivity, as defined by the --time option, the command terminates"; the default is 5 seconds. The service exposes "a compatibility layer offering support for the Docker v1.40 API, and a Podman-native Libpod layer", so a Docker client or docker-compose can be pointed at it by setting DOCKER_HOST to the Podman socket path (the exact value is in the listing above). Compatibility is with that API version; a tool that needs a newer Engine API feature will fail against the compatibility layer. Our reading of the documented behaviour is that this API gap, not the CLI, is where a "drop-in" migration is most likely to stall.
Compose, pods and the Kubernetes on-ramp
Docker Compose is a first-party CLI plugin: "Compose v2, announced in 2020, is written in Go and is invoked with docker compose", and "Compose v5, released in 2025, is functionally identical to Compose v2" with a Go SDK added. Podman's compose is a shim by design: podman compose "is a thin wrapper around an external compose provider such as docker-compose or podman-compose", and "if installed, docker-compose takes precedence since it is the original implementation of the Compose specification". The wrapper "will emit a warning saying that it executes an external command" unless you silence it. So Compose on Podman is the Docker Compose binary talking to the Podman socket, with the API compatibility caveat from the previous section.
What Podman has instead is Kubernetes shapes. Pods are native, and podman kube play "reads in a structured file of Kubernetes YAML" and recreates it, supporting Pod, Deployment, PersistentVolumeClaim, ConfigMap, Secret, DaemonSet and Job kinds and five volume types. podman kube generate goes the other way, producing YAML from running containers or pods. For a team whose destination is Kubernetes, that is a shorter path than Compose; for a team whose destination is a single host, Compose remains the better-documented tool and Podman runs it at one remove.
Laptops: Desktop licensing and podman machine
Neither engine runs Linux containers natively on macOS or Windows; both need a VM. Docker's VM is Docker Desktop, licensed under the Docker Subscription Service Agreement: free for "small businesses (fewer than 250 employees AND less than $10 million in annual revenue)", personal use, education and non-commercial open source, and requiring a paid subscription for "professional use in larger organizations" and government entities. The docs note that "the licensing and distribution terms for Docker and Moby open-source projects, such as Docker Engine, aren't changing"; the licence is about Desktop, not the engine on your servers.
Podman's VM is podman machine: "Podman on MacOS and Windows requires a virtual machine", managed by podman machine init and start, with libkrun the default provider on macOS and WSL the default on Windows; "all podman machine commands are rootless only". Podman Desktop is a separate graphical application under Apache-2.0. The trade is a licence conversation against a VM you administer yourself, and for a company above the Desktop thresholds that conversation is often the real reason this comparison is being read.
What to run where
Which engine, by situation
| Situation | Run | Why | What to plan for |
|---|---|---|---|
| Long-lived services on Linux hosts you administer with systemd, no orchestrator | Podman with Quadlet | Units, restarts, dependencies and logs are systemd's; no privileged daemon on the host | Rewrite --restart into [Service] Restart=; put units under /etc/containers/systemd; AutoUpdate needs fully qualified image names |
| CI runners, agents or platforms that mount docker.sock and expect the Engine API | Docker | The socket and the current Engine API are the contract those tools were written against | Treat socket access as root; rootless Docker moves the socket to $XDG_RUNTIME_DIR and breaks hard-coded paths |
| Developer laptops at a company above the Desktop free tier | Podman (podman machine or Podman Desktop) | No per-seat subscription; same images, same Containerfile | A VM per laptop to keep patched; podman compose runs docker-compose against the socket, so test the compose files you actually use |
| Multi-tenant build or shared hosts where users must not get root through containers | Podman rootless | Rootless is the ordinary mode; nothing setuid beyond newuidmap/newgidmap; no daemon to hand out root | Provision subuid/subgid ranges for every user; sub-1024 ports and cgroup v1 limits are off the table |
| Compose-first applications you deploy as a unit and manage with docker compose commands | Docker | First-party Compose v5 plugin with the full Compose Specification and the Engine behind it | The daemon runs as root unless you also do the rootless setup, with its storage and networking limits |
| Workloads headed for Kubernetes, prototyped on a single host | Podman | Native pods; kube play and kube generate move YAML in both directions | Only a subset of kinds and volume types is supported; treat kube play as a dev loop, not a cluster |
Trade-offs, with their prices
A root daemon buys you one supervisor that owns restarts, live-restore, the socket and an API every tool already speaks. The price is a root process whose socket is equivalent to root, so socket access has to be handed out like a privilege and audited like one. That lands hardest on anyone running shared CI and on anyone whose monitoring or agents mount docker.sock.
No daemon buys you the opposite: no privileged long-running process, rootless without a setup script, and no engine process whose restart takes the containers with it. In exchange, supervision moves to systemd, the API is opt-in and socket-activated with a five-second idle timeout by default, and the compatibility layer targets Docker API v1.40. Every docker.sock consumer therefore becomes a migration item, which is a platform team's problem before it is anyone else's.
Rootless, on either engine, means a container escape lands in an unprivileged user namespace. It also means storage driver restrictions, user-mode networking, no checkpoint, no sub-1024 ports without a sysctl, and cgroup limits only on v2 with systemd. The host has to be prepared first (subuid ranges, kernel version, cgroup v2), so whoever owns base images and host builds is deciding this before the container team is.
On Compose, Docker owns the product and Podman runs the same compose files through the same docker-compose binary without the daemon, at the cost of one more layer that emits a warning and depends on API compatibility. Teams whose deployment unit is a compose project are the ones who will feel the difference.
On laptops, Docker Desktop is a supported product with settings, updates and extensions, paid for by subscription above 250 employees or $10M in revenue; podman machine removes the licence conversation and hands you a VM to operate. That trade concerns engineering managers and procurement more than it concerns engineers.
Before you alias docker=podman
Where we land: on servers we administer, Podman under systemd, rootless wherever the host can support it, with the socket enabled only on hosts that need the API. Where a tool chain is built around the Docker daemon and its API, Docker, run rootless where its documented limits allow and with socket access treated as root either way. The alias is fine for people; it is the automation that needs the audit.