If you're running K8s in production, you're already using containerd as the container runtime anyway. Docker Desktop just adds an extra layer on top of what you already have. Nerdctl lets you talk directly to containerd with commands that work exactly like Docker.
The Problem Nerdctl Solves
Docker's architecture is annoying as hell. Every docker run
command goes through the Docker daemon (dockerd), which then talks to containerd, which actually runs your shit. That's two layers when you only need one. I've spent way too many hours debugging Docker daemon issues that turn out to be containerd problems in disguise.
In K8s clusters, containerd runs your pods directly. But if you want to debug a container or build an image on a K8s node, you're stuck with the low-level ctr
command that nobody wants to learn. Nerdctl gives you Docker commands that work directly with containerd.
What Actually Works
The good news is most Docker commands work identically:
nerdctl run -it ubuntu bash
- works exactly like Dockernerdctl build -t myapp .
- builds images with BuildKitnerdctl compose up
- runs your docker-compose filesnerdctl ps
- lists containers
Your muscle memory from Docker just works. No need to learn new commands or change your scripts.
Gotchas You'll Hit
Setup is More Involved
Unlike Docker Desktop which "just works", nerdctl requires you to install and configure several components:
- containerd daemon
- CNI networking plugins
- BuildKit for image builds
- Various snapshotters for different features
The nerdctl-full tarball bundles most dependencies, but you'll still need to configure networking and systemd services. I spent 3 hours figuring this out the first time because the docs assume you already know containerd inside and out. Spoiler: nobody does.
On RHEL/CentOS 8, the default firewall rules block CNI networking. Ubuntu 22.04 works fine but 20.04 has SELinux issues with rootless mode.
CNI Networking Can Be a Pain
Docker handles networking automatically. With nerdctl, you need to understand CNI plugins. The default bridge network might not work the way you expect. Port publishing works differently. If you've never configured CNI plugins before, expect to spend time reading documentation that assumes you're already a networking expert. I wasn't, and it showed - spent an entire afternoon trying to get port forwarding to work properly.
BuildKit Integration is Finicky
Image building requires BuildKit daemon running separately from containerd. Sometimes BuildKit fails to start for no obvious reason. Sometimes it runs out of disk space in /tmp
and fails silently. The error messages are complete garbage when this happens - you'll get cryptic bullshit like "failed to solve: rpc error: code = Unknown desc = failed to receive status" that tells you fuck-all about what actually broke. I've wasted entire afternoons on this exact error.
BuildKit version 0.11+ changed the default socket path from /run/buildkit/buildkitd.sock
to something completely different, so if you upgrade containerd but not BuildKit (or vice versa), everything breaks with zero indication of what's wrong. Found this out the hard way during a system update.
Some Docker Features Don't Exist
- No equivalent to Docker Desktop's GUI
- Volume mounting syntax is slightly different
- Some docker-compose features have edge cases
- Third-party tools that expect Docker specifically won't work
Advanced Features That Are Actually Useful
Direct Kubernetes Access
This is the killer feature. On a K8s node, you can debug pods directly:
nerdctl --namespace k8s.io ps
nerdctl --namespace k8s.io exec -it pod-name bash
No need to set up kubectl or deal with registries for debugging. This alone justifies using nerdctl if you're debugging K8s issues regularly.
Lazy Pulling with Stargz
Large images start faster because nerdctl can pull layers on-demand. Works great for machine learning images that are 10GB+ but you only need the first few layers to start.
Image Encryption
Built-in support for encrypted container images. Actually useful for regulated environments where you need encryption at rest.
Current Version Status
Version 2.1.4 came out like a week ago. Still actively developed, which is good because containerd keeps changing shit and breaking backwards compatibility.