Yasas Banuka
Why Docker Exists (And Why VMs Weren't Enough)
Back to BlogDevOps

Why Docker Exists (And Why VMs Weren't Enough)

May 15, 20268 min read·1,587 words
Share
Docker: From Zero to Production — Part 1 of 7
1Why Docker Exists (And Why VMs Weren't Enough)Current2What's Actually Happening When You Run a Container3Images, Layers, and Why Your Builds Are Slow
4Docker Networking: How Containers Actually Find Each OtherComing Soon
5Docker Compose: Reading the File You've Been Copy-PastingComing Soon
6Securing Your Docker Setup (Most People Skip This)Coming Soon
7From Laptop to Production: CI/CD with DockerComing Soon

Picture this.

It's 2013. A young French programmer named Solomon Hykes walks onto a stage at PyCon - one of the biggest Python conferences in the world. He isn't a keynote speaker. He isn't even scheduled for a proper talk slot.

He gets 5 minutes. A lightning talk.

He demos something called Docker. He shows the crowd how you can package an application and everything it needs into a neat little container, and then run it anywhere-your laptop, a bare-metal server, the cloud-with total consistency.

At the 5-minute mark, the organizers cut his microphone off.

But the audience didn't care. They'd seen enough.

That demo - abruptly silenced mid-sentence - went up on YouTube, hit the top of Hacker News, and spread like wildfire through the developer community. Within months, teams were running Docker in production environments before the creators even felt it was ready.

As Solomon later recalled: "We told people to wait, and they ran it anyway."

That doesn't happen unless a tool solves a pain people know deeply and personally.

So what exactly was that pain?

The original PyCon 2013 demo — 5 minutes that changed how we ship software.

The Problem Every Developer Knows

You've felt this. Even if you didn't have a name for it.

You spend days building a feature on your laptop. The tests pass. The app compiles. Everything works flawlessly. You push it to the staging server, and it instantly crashes.

Wrong version of Node. Missing C++ library. Different Linux distribution. A ghost environment variable that exists on your machine but nowhere else. The error stack trace makes absolutely no sense. You spend two days debugging infrastructure instead of writing code.

"Works on my machine."

This phrase became the running joke of a generation of software engineers. It was funny because it was universally, agonizingly true.

Now, multiply that single failure across an entire organization:

  • 5 developers with slightly different local setups
  • A staging environment that drifted away from production months ago
  • A production server that gets its packages updated independently
  • Three different microservices, each demanding conflicting runtime versions

Every deployment turned into an archaeology project. You weren't just shipping code anymore - you were shipping a fragile, invisible web of assumptions.

The First Attempt: Virtual Machines

The industry actually had a solution for this long before Docker arrived.

Virtual Machines (VMs).

In theory, the idea was brilliant: instead of hoping the server's environment matches your code's expectations, just ship the entire environment. Pack up an operating system, your runtime, your libraries, your code - all of it - into one massive file. Run it anywhere.

And it worked. Mostly.

But VMs came with a hidden tax that developers hated. A VM isn't just a box your app lives in. It is a full computer simulation. It has its own operating system kernel, its own virtualized memory management, and its own fake hardware. All of that has to run on top of the real machine's hardware through a hypervisor.

Imagine you want to move to a new city for work. A VM is like picking up your entire house - foundations, plumbing, wiring, roof - and physically moving it to the new city. It works, but the truck is enormous, moving day takes hours, and you're paying to transport a lot of stuff you could have just found in the new city.

The numbers tell the real story. On a standard server with 128GB of RAM:

  • You could comfortably run maybe 10-15 virtual machines.
  • Each VM eats 600MB+ just to boot its guest OS - before your app even starts.
  • Startup time is measured in minutes, not seconds.

And the problem wasn't just resource bloat. It was rigidity. Once you configured a VM, it was heavy, slow to spin up, and painful to replicate perfectly. Developers still had to write lengthy, brittle bash scripts and pray that what worked on the staging VM matched the production VM.

The "works on my machine" problem hadn't been solved. It had just moved one layer deeper.

The Insight That Changed Everything

Here's the thing nobody initially noticed about VMs: most of that weight was completely unnecessary.

Every single VM was carrying its own operating system kernel. But here is the question that unlocked the container revolution:

Why does every app need its own kernel?

The kernel - the core of the OS that manages hardware, memory, and CPU processes - is already running on the host machine. It's shared infrastructure, like the plumbing in an apartment building. Why does every tenant need to install their own water main?

What if you could keep the isolation (each app stays in its own private box) but ditch the duplication (no separate OS per box)?

Docker's answer: a container doesn't carry a kernel. It borrows the host's.

The container gets total isolation - its own filesystem, its own network stack, its own process space, but the heavy lifting is handled by the existing host kernel.

Now, instead of dragging your whole house to the new city, you just pack a suitcase. You bring your clothes and your laptop. The city already has buildings, plumbing, and electricity. You only bring what is uniquely yours.

The suitcase is your container. The city's infrastructure is the host kernel.

Architecture Comparison

Virtual Machines

Physical Hardware
Hypervisor (VMware / KVM)
Guest OS
Kernel
App
Guest OS
Kernel
App
Guest OS
Kernel
App
Heavy · Slow boot · 3 kernels

Containers

Physical Hardware
Host OS + Shared Kernel
Docker / containerd
libs
App
libs
App
libs
App
Lightweight · ms boot · 1 kernel

The Efficiency Leap

Let's make this concrete. Imagine running three apps on one server: a Node.js API, a Python data processor, and an Nginx web server.

With VMs: You are running three separate virtual computers. Three full OS installations. Your server is burning a huge chunk of its RAM and CPU just keeping those three mini-computers alive, before your apps process a single user request.

With Containers: You have three isolated boxes sharing one kernel. The server's power goes almost entirely to your actual apps. And because there's no bulky OS to boot up, each container starts in milliseconds.

On that same 128GB server where you could only run 15 VMs, you can now comfortably run 400–500 containers.

That isn't just an efficiency gain. It's a fundamentally different way to compute.

Tip

A container starts instantly because there is no OS to boot. It's just a new, isolated process using the kernel that's already running. Think of it as the difference between booting up a whole new laptop (VM) versus simply opening a new tab in your browser (Container).

What Docker Actually Did

Here is the most fascinating part of the origin story: Docker didn't invent containers.

The underlying Linux features that make containers possible - namespaces for isolation, cgroups for resource limits, union filesystems for layered images - had existed in the Linux kernel for years. Google had been using similar technology internally since the mid-2000s.

But nobody outside of hardcore systems experts could use it. It required deep Linux kernel knowledge, complex configuration files, and a whole lot of duct tape.

What Solomon Hykes and the Docker team did was make it accessible. They built a clean, developer-friendly interface on top of these powerful but complex Linux features. A simple CLI. A standard format for packaging apps (the image). A registry for sharing them (Docker Hub).

They didn't build the engine. They built the steering wheel, the pedals, and the dashboard.

As Solomon himself put it: "Docker happened to see the opportunity and shift the right tools at the right time... It was going to happen anyway, with or without Docker."

The Dockerfile That Didn't Exist

Here is a lesser-known fact that perfectly captures Docker's explosive growth: the Dockerfile wasn't even in the original launch.

The file we now use to define exactly how our app's environment gets built was added later as a "simple experiment." As Solomon admitted: "It was taped together and not in the original version. It became popular, so we had to fix it."

The community grabbed it and ran. Teams started writing Dockerfiles before Docker's own engineers had even stabilized the feature. The tool got pulled forward by the sheer desperation of the people using it.

Any rough edges were instantly forgiven because the core value was so obvious.

Where This Leaves Us

Let's step back and look at the arc.

The problem was old and painful: software is fragile because environments differ. Your app isn't just your code; it's your code plus every assumption it makes about the world around it.

VMs tried to solve this by shipping the whole world. It worked, but it was expensive, slow, and bloated.

Docker solved it by realizing you only need to ship your app's specific world. Just the filesystem, the libraries, and the runtime. The kernel is already there.

The result? Portable, lightweight, fast, and mathematically consistent environments that you can run anywhere and share with anyone.

And it all started with a 5-minute talk that got cut off.

What's Next

This was the "why." In the next article, we are going to go one level deeper, into what is actually happening inside your machine when you type docker run.

Spoiler: it involves three Linux kernel features that existed long before Docker, doing some genuinely clever things. Once you see how they work, containers will never feel like magic again. They'll just feel obvious.


Did you have a "works on my machine" moment that cost you real time? Drop it in the comments — I'd love to hear the most painful ones.

#docker#containers#devops#linux#history
Share
← All Articles
Yasas Banuka Malavige

Written by

Yasas Banuka Malavige

DevOps Engineer · Building resilient infrastructure, automating pipelines, and documenting the quiet foundations that keep production systems alive.

Docker: From Zero to Production — Part 1 of 7
1Why Docker Exists (And Why VMs Weren't Enough)Current2What's Actually Happening When You Run a Container3Images, Layers, and Why Your Builds Are Slow
4Docker Networking: How Containers Actually Find Each OtherComing Soon
5Docker Compose: Reading the File You've Been Copy-PastingComing Soon
6Securing Your Docker Setup (Most People Skip This)Coming Soon
7From Laptop to Production: CI/CD with DockerComing Soon