We’ve all had that moment: you click on Docker Desktop, your laptop fan immediately sounds like a jet taking off, and RAM usage shoots past 2 GB before you even spin up a hello-world container. Right on cue, that licensing pop-up shows up to remind you that your company needs a paid plan just to keep running it.

That’s usually when people open a search tab and start looking for an exit strategy. The upside is that the container world has grown up a ton recently. Ditching Docker Desktop doesn't mean hacking together an unstable setup anymore; the alternatives are genuinely solid.

Quick Comparison: Which Tool for Which Situation

Tool macOS/Win GUI Linux Free (Commercial) Kubernetes Rootless Docker Socket
Docker Desktop Yes No No (>250 emp.) Yes No Yes
Rancher Desktop Yes No Yes Yes Yes Yes
OrbStack Yes No No ($8/mo) Yes Yes Yes
Podman CLI only Yes Yes Via pods Yes With config
nerdctl + containerd CLI only Yes Yes No Yes No
CRI-O No Yes Yes Yes (only) Yes No
  • If you're on a Mac and want a zero-cost drop-in for Docker Desktop: Go with Rancher Desktop.
  • If you work mostly in Linux or care about production parity: Podman is your best bet.
  • If you're tuning CI/CD build runners: Take a look at Podman or nerdctl + containerd.
  • If you just want the absolute fastest engine on macOS and don't mind paying: Grab OrbStack.

Why Developers Are Moving Away from Docker Desktop

The big migration kicked off when Docker updated its licensing terms back in January 2022. If your company employs more than 250 people or brings in north of $10M in revenue, you're on the hook for a paid license. The underlying engine (docker CLI on Linux) is still free, but the desktop wrapper you rely on day-to-day is no longer free for enterprise use.

Licensing aside, Docker Desktop carries some architectural baggage that people are getting tired of. Its background daemon runs with root privileges, meaning a security flaw inside a container could theoretically let an attacker compromise your whole machine. Modern alternatives were built from the ground up to solve that exact security blind spot.

Podman: Daemonless, Rootless, Drop-In Compatible

Podman is basically Red Hat’s take on doing containers without a bloated root daemon. Its core design is completely different: there’s no massive background daemon running 24/7. Instead, every container simply executes as a regular child process under your own user profile.

Bash

code9 lines
# Install on Fedora/RHEL
sudo dnf install podman

# Install on Ubuntu/Debian
sudo apt install podman

# macOS (via Homebrew)
brew install podman
podman machine init && podman machine start

For basic day-to-day commands, you barely have to change your habits:

Bash

code4 lines
alias docker=podman
podman run -d --name web -p 8080:80 nginx
podman ps
podman build -t my-app .

Where alias docker=podman actually breaks down

Setting up a shell alias handles straightforward CLI commands, but in real workflows, a few rough edges usually pop up:

  1. Anything wired to the Docker socket. Tools like Testcontainers, VS Code Dev Containers, and local CI scripts expect /var/run/docker.sock to exist. Podman isolates this into an unprivileged user path:

Bash

code5 lines
# Enable Podman socket
systemctl --user enable --now podman.socket

# Point tools to it
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
  1. docker-compose quirks. Podman doesn’t ship with a native Compose engine out of the box. You can either install community-built podman-compose, or point podman compose to the standard Docker Compose binary if it’s already installed on your system.

Bash

code2 lines
pip install podman-compose
podman-compose up -d

Standard docker-compose.yml configs generally run fine, but watch out for services configured with network_mode: host. Because rootless mode isolates your user namespace, mapping ports below 1024 behaves differently than standard Docker.

  1. Dockerfile USER instructions. If you have USER root inside your build definition, rootless Podman maps that to your unprivileged user account on the host system rather than actual root. While this keeps things secure, it can trip up build scripts that genuinely need root-level host privileges.

Pods: the Podman feature Docker doesn't have

One very neat trick Podman brings to the table is pods. You can bundle multiple containers together to share a single network space, exactly how Kubernetes does it. That makes it super handy for testing pod networking on your local machine before deploying manifests to a remote cluster:

Bash

code3 lines
podman pod create --name app-pod -p 8080:80
podman run -d --pod app-pod --name web nginx
podman run -d --pod app-pod --name sidecar my-proxy

Best for: Linux daily drivers, teams deploying onto RHEL/Fedora environments, security-first infrastructure, and folks building heavily around Kubernetes.

nerdctl + containerd: Docker CLI on a Leaner Runtime

containerd is the low-level engine that Docker originally built and then handed over to the CNCF; it’s what powers modern Kubernetes clusters on GKE, EKS, and AKS today. nerdctl is basically a familiar Docker-style CLI layer sitting directly on containerd, cutting out the middleman entirely.

Bash

code4 lines
nerdctl run -d --name web -p 8080:80 nginx
nerdctl ps
nerdctl build -t my-app .
nerdctl compose up -d

The syntax and flags mirror what you already know. The real day-to-day wins come down to two features:

Lazy pulling. nerdctl plays nice with image formats like eStargz. Instead of waiting for a massive multi-gigabyte image to download layer by layer, your container boots almost immediately using just the chunks required to get the process running. In CI builds, that cuts initialization wait times down to almost nothing.

Clean CI builds without security bypasses. Running Docker-in-Docker often forces teams to slap a dangerous --privileged flag onto their pipelines, leaving host nodes exposed. With containerd-native runners (like standard GitHub Actions or GitLab containerd setups), you bypass that risk completely.

Where nerdctl falls short

The big catch here is that nerdctl doesn't provide a drop-in Docker API socket. If your workflow relies on tools expecting to talk to docker.sock over an API, nerdctl won't work out of the box. For socket-dependent setups, stick with Podman or Rancher instead.

Best for: Kubernetes-heavy teams, automated build pipelines, and anyone wanting fast features like lazy image pulling without having to relearn a new CLI.

Rancher Desktop: The Free Docker Desktop Replacement

If you work on macOS or Windows and really want a traditional GUI app without licensing fees, Rancher Desktop is the cleanest direct alternative. It bundles together containerd (or dockerd), nerdctl, kubectl, and an entire lightweight k3s cluster right out of the box.

What makes it practical day-to-day:

  • Let you toggle between containerd and dockerd from the settings panel without reinstalling anything.
  • Exposes a standard Docker socket immediately, so VS Code Dev Containers and Testcontainers work smoothly.
  • Bundles nerdctl compose out of the gate for existing Compose files.
  • Gives you a full local Kubernetes cluster that you can wipe clean with a single button.

macOS local Kubernetes cluster setup:

Bash

code8 lines
# After installing Rancher Desktop, k3s is already running
kubectl get nodes
# NAME                   STATUS   ROLES                  AGE
# lima-rancher-desktop   Ready    control-plane,master   2m

# Deploy locally
kubectl apply -f deployment.yaml
kubectl port-forward svc/my-service 8080:80

There's no extra tinkering required. The Kubernetes cluster runs directly inside the same Lima virtual machine as your containers, which means any image you build locally with nerdctl is immediately ready to run on k3s without pushing to a registry.

It generally feels much snappier on a Mac than Docker Desktop. Rancher Desktop relies on Lima, a very lightweight open-source VM framework, instead of Docker’s heavier proprietary hypervisor layer. You’ll feel the difference most during cold boots and disk-heavy file operations.

Best for: Mac and Windows users who want an all-in-one local setup with a friendly UI and zero licensing strings attached.

OrbStack: macOS Performance Benchmark

OrbStack isn't free for company use ($8/month once the 90-day trial runs out), but right now it is hands-down the fastest way to run containers on a Mac. Its virtualization layer is noticeably leaner than what you get from both Docker Desktop and Rancher Desktop.

What that translates to in practice is hard to ignore:

  • Cold boots: containers launch in roughly 1 to 2 seconds compared to the 5 to 8 seconds Docker Desktop often takes.
  • Memory: baseline idle RAM stays around 300 to 500 MB rather than swallowing 2 GB right off the bat.
  • File system sync: mounted directory reads and writes are remarkably fast, which makes a world of difference when you run dev servers listening for live file changes.

It includes a fully compatible Docker socket, meaning all your existing IDEs and extensions pick it up instantly without tweaking configs.

Best for: macOS developers who spend their entire day in containers and want top-tier performance. If you bill your hours, the monthly cost easily pays for itself in avoided frustration.

CRI-O: Kubernetes Runtime, Not for Local Dev

CRI-O is built specifically to implement Kubernetes' Container Runtime Interface and nothing else. You won't find a casual CLI, image building tools, or a registry manager here; it's strictly the engine that sits under kubelet on production cluster nodes.

If you manage Kubernetes infrastructure (like OpenShift, where it’s the default), it’s good to know CRI-O exists because it explains why things occasionally behave differently in production than on local Docker. You definitely wouldn't install this on a laptop for day-to-day dev work. But when you’re troubleshooting a weird staging bug, checking the underlying runtime is a smart first sanity check:

Bash

code2 lines
# On a CRI-O node
crictl info | grep -i version

Common Migration Issues

Testcontainers with Podman

Since Testcontainers naturally hunts for the standard Docker daemon socket, you'll want to point it explicitly toward Podman:

Bash

code3 lines
# In your test environment config or .testcontainers.properties
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
export TESTCONTAINERS_RYUK_DISABLED=true

You also need to disable Ryuk (the auto-cleanup container). Its process model conflicts with rootless setups, so let your testing framework clean up its own resources after test runs finish.

VS Code Dev Containers with Rancher Desktop

Rancher Desktop handles socket routing automatically most of the time. If VS Code is still having trouble finding the engine, you can point it directly to the socket path in your user settings:

Bash

code5 lines
# Check where Rancher Desktop puts its socket
ls ~/.rd/docker.sock

# Add to VS Code settings.json
"docker.host": "unix:///Users/YOUR_USERNAME/.rd/docker.sock"

Networking: rootless containers and ports below 1024

Rootless engines (Podman, or rootless nerdctl) won't let you bind low ports like 80 or 443 out of the box because the Linux kernel blocks unprivileged users from touching them. You can handle this in two ways:

Bash

code6 lines
# Allow unprivileged port binding (Linux)
sudo sysctl net.ipv4.ip_unprivileged_port_start=80

# Or map to a high port and use a reverse proxy
podman run -p 8080:80 nginx
# then nginx/caddy on the host forwards :80 -> :8080

Volumes and SELinux

If you're on a distribution like Fedora or RHEL with SELinux turned on, local directory mounts will often fail silently because of access restrictions. You just need to pass the :z or :Z flag:

Bash

code3 lines
podman run -v /host/path:/container/path:z my-image
# :z  = shared across multiple containers
# :Z  = locked exclusively to this container

This is by far one of the most common surprises when moving to Podman. Docker Desktop never complained about it simply because its background daemon runs as root and skips those permission checks altogether.