Here's the thing about Docker: it solved the most annoying problem in software development. You know how your Node.js app works perfectly on your MacBook but crashes immediately on the staging server because it's running Ubuntu 22.04 with a different version of everything? Docker packages your app with the exact versions of everything it needs, so it runs the same everywhere.
The Real Problem Docker Solves
Before Docker, deploying software was a fucking nightmare. You'd spend hours debugging why your app worked locally but failed in production. Different Python versions, missing libraries, conflicting dependencies - it was endless.
I've personally spent entire nights troubleshooting deployment issues that turned out to be because prod was running Python 3.8.10 while my local machine had 3.9.7. Docker eliminates this by packaging your code with the exact runtime it needs.
Containers share the host kernel but isolate everything else. Your app thinks it's running on its own machine, but it's actually just a process with fancy isolation. This means you get near-native performance without the overhead of full VMs.
How Docker Actually Works (The Technical Bits)
Docker uses Linux namespaces and cgroups to create isolated environments. The Docker daemon (dockerd
) manages containers, images, networks, and volumes on your system.
Here's what happens when you run docker run
:
- Image Pull: Docker pulls the image from a registry (usually Docker Hub)
- Container Creation: Creates a new container from that image
- Filesystem Setup: Sets up a read-write layer on top of the image
- Network Setup: Creates network interfaces and assigns IP addresses
- Process Start: Runs your application as PID 1 inside the container
The genius is in the layered filesystem. Images are built in layers, so if you update just your application code, Docker only needs to rebuild that layer. Base OS layers get cached and reused across containers.
Production Reality Check
Docker Desktop is now a paid product - Pro plans cost $9/month, Team is $15/month per user. They made this change in December 2024, which pissed off a lot of developers who had been using it free.
In production, you're probably not running Docker Desktop anyway. You're using the Docker Engine directly on Linux servers, which is still free and open source.
Container networking can be a pain in the ass. The default bridge network works fine for simple cases, but once you need containers talking to each other reliably, you'll be diving into custom networks and service discovery.
Docker Hub rate limiting broke half the internet's CI pipelines in 2020. Free accounts get 100 pulls per 6 hours, which sounds like a lot until you're running integration tests that pull base images.
The Gotchas Nobody Tells You
Memory limits are enforced hard: Set --memory=512m
and your container gets killed with exit code 137 when it tries to use 513MB. No warnings, just dead. Here's a real GitHub issue about this exact problem.
File permissions are a nightmare: Files created in containers often have weird ownership, especially with volume mounts. You'll spend time fighting with chown
and understanding user namespaces.
Windows containers still suck: If you're on Windows and need to run Windows containers, prepare for pain. The tooling is years behind Linux containers. Windows Server 2022 containers are better but still janky.
BuildKit cache gets corrupted: Sometimes Docker's build cache gets corrupted and builds become slow as hell. docker builder prune -af
usually fixes it, but you'll lose all cached layers. I've seen this happen after Docker Desktop updates especially.
Exit code 125 means your Dockerfile is fucked: When you see this error, it's usually a syntax error in your Dockerfile or you're trying to run a command that doesn't exist in the image.
Despite the frustrations, Docker solved a real problem. The "works on my machine" era is basically over. When you're debugging a production issue at 3 AM and the container runs exactly the same locally as it did in production, you'll remember why we put up with all this complexity.