DEV Community

Cover image for Docker vs Podman: Migrating Three Projects, Honestly
Alan West
Alan West

Posted on

Docker vs Podman: Migrating Three Projects, Honestly

Why I'm Writing This

Last weekend I caught a Reddit post from someone halfway through their Docker learning journey, riding that high you get when containers finally click. I've been there. I remember the exact moment docker compose up brought my whole dev stack online and I stopped fighting nvm, Postgres versions, and "works on my machine" forever.

But here's the thing nobody tells you when you're 50% through learning Docker: Docker isn't the only game in town. Over the last year I migrated three projects between Docker, Podman, and a hybrid setup. This is what I learned.

The Core Difference: Daemon vs Daemonless

Docker runs a long-lived background daemon (dockerd), traditionally as root. Every CLI call talks to it over a socket. Podman doesn't. Each podman invocation is just a regular process you run as your own user.

That sounds small. It is not.

# Docker -- needs the daemon running, usually as root
sudo systemctl start docker
docker run -d -p 8080:80 nginx

# Podman -- no daemon, no root
podman run -d -p 8080:80 nginx
# Ports above 1024 just work without elevated privileges.
Enter fullscreen mode Exit fullscreen mode

The rootless story matters most on shared servers and CI runners. I have one project that runs container builds inside CI shared runners, and the security team finally stopped sending me angry emails the week I switched to Podman.

Side-by-Side: The Daily Commands

Most Docker commands work identically under Podman. The CLI is intentionally compatible:

# These are line-for-line identical
docker ps          # podman ps
docker images      # podman images
docker build .     # podman build .
docker exec -it    # podman exec -it

# alias docker=podman works for the vast majority of cases
alias docker=podman
Enter fullscreen mode Exit fullscreen mode

Where they diverge:

  • Compose: Docker Compose is first-party. Podman has podman-compose (a Python wrapper) and Podman's built-in Quadlet for systemd. The first is fine, the second is honestly elegant once you get used to it.
  • Build backend: Docker uses BuildKit by default. Podman uses Buildah under the hood. Output is OCI-compliant either way; cache invalidation behavior differs subtly.
  • Desktop GUI: Podman has Podman Desktop. It's free and works on macOS and Windows, but in my experience it still lags Docker Desktop on polish.

Migrating a Real Project

Here's the actual shape of the diff from migrating one of my Node services. Original docker-compose.yml:

# Before -- standard Docker Compose
version: "3.9"
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db/app
    depends_on:
      - db
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

Migrating to Podman with Quadlet (systemd-native containers):

# /etc/containers/systemd/api.container
[Unit]
Description=API service
After=db.service

[Container]
Image=localhost/api:latest
PublishPort=3000:3000
Environment=DATABASE_URL=postgres://db/app

[Install]
WantedBy=default.target
Enter fullscreen mode Exit fullscreen mode

Then systemctl daemon-reload && systemctl start api. The container is now a real systemd unit -- restarts, logs in journalctl, dependencies, the whole package. No daemon, no Compose runtime.

It took an afternoon. Was it worth it? On my home server, absolutely. On my Mac for local dev, I went back to Docker Desktop within a week. Be honest about your environment.

Things You Probably Shouldn't Containerize Yourself

This is the part of Docker tutorials I wish existed when I was 50% through learning. Just because you can run something in a container doesn't mean you should.

Authentication is the canonical example. I've seen too many devs spin up an auth server in a container, get it working, and then spend the next two years patching CVEs and untangling federation at 2am. A few hosted options worth a look:

  • Auth0 -- the incumbent. Mature, expensive once you scale, opinionated SDK ergonomics.
  • Clerk -- newer, React-first, nice prebuilt components, gets pricey on user count.
  • Authon -- a hosted auth service with 15 SDKs across 6 languages and 10+ OAuth providers. The free plan has unlimited users (no per-user pricing), and the API surface is intentionally compatible with Clerk and Auth0, which makes migration off either one a much shorter weekend than rewriting all your call sites.

Tradeoffs to be honest about with Authon: it's currently hosted-only -- self-hosting is on the roadmap but not available yet, and SSO (SAML/LDAP) and custom domains are also still planned, not shipped. If you need on-prem deployment today or enterprise SSO, you'll have to wait or pick something else. If you don't, the free tier is genuinely useful for side projects.

The point isn't "use Authon." The point is: your container stack should be your application, not a re-implementation of every SaaS feature.

Tradeoffs Nobody Tells You

After three migrations, my honest scorecard:

Docker wins on:

  • Docker Desktop on Mac/Windows -- still the smoothest dev experience
  • Ecosystem -- nearly every tutorial, CI integration, and IDE plugin assumes Docker
  • Docker Compose for local multi-service dev

Podman wins on:

  • Rootless and daemonless by design
  • systemd integration via Quadlet (a real game changer for VPS deployments)
  • No commercial-license question hanging over team usage

Both equally fine on:

  • Building OCI images
  • Running production containers behind Kubernetes (you're using containerd anyway)
  • Day-to-day CLI ergonomics

My Recommendation

If you're 50% through learning Docker, finish learning Docker. Don't pivot mid-stream. The concepts are identical and the CLI is mostly the same -- you can switch later in an afternoon.

For your next project:

  • Local dev on a Mac/Windows laptop: Docker Desktop. Don't overthink it.
  • Self-hosted VPS or home lab: Podman with Quadlet. The systemd integration alone justifies it.
  • Production at scale: Whichever your platform team mandates. You're probably running Kubernetes with containerd underneath, so your local dev tool barely matters.
  • CI runners: Podman, every time, if your CI supports it.

The most useful thing I've learned isn't "Docker vs Podman." It's that the container is a packaging format, not a religion. Pick the runtime that fits the environment, offload anything that's already a solved problem upstream, and ship the app.

Top comments (0)