Why Containers Exist

The packaging problem containers actually solve, why a container is not a small virtual machine, and the honest list of things Docker is the wrong answer for.

beginner 16 min lesson hands-on task included

“It works on my machine” is not a joke about carelessness. It is a statement about an undocumented dependency graph: a glibc version, an environment variable, a package someone installed by hand two years ago. Containers exist to make that graph explicit and shippable.


Topic 1: The Problem Before Containers

A decade of application deployment had a recurring shape:

  • Applications were monolithic — one stack, one artefact, deployed to one server.
  • Scaling meant buying more servers and hoping the traffic estimate held.
  • The runtime environment was configured by hand, or by a configuration-management tool that drifted anyway.
  • Promoting code from dev → test → prod crossed three subtly different environments.

Take a concrete case from the insurance world. A web portal runs on three servers handling roughly 15,000 parallel requests. Tax-filing season arrives and the real number is 25,000. You need five servers by Monday. Provisioning them is a ticket, an install run, and a prayer that the new boxes match the old ones.

The industry answer was microservices — break the application into independently deployable services so each can be scaled on its own. That answer creates a new problem immediately: you now have twenty artefacts, each with its own runtime, its own dependencies, and its own version of “works on my machine”.

Shipping containers are the analogy the industry chose, and it is a good one. Before standard containers, moving goods meant a bespoke handling process per cargo type. The standard box did not make ships faster — it made every cargo interchangeable to every crane. Docker does that for software: one packaging format, one set of verbs (build, ship, run), regardless of whether the payload is Java, Go or PHP.


Topic 2: What a Container Actually Is

A container is a process — an ordinary Linux process — with a restricted view of the system.

That view is restricted by two kernel features you will meet properly in lesson 4:

FeatureWhat it does
NamespacesIsolate what the process can see: its own PID tree, network interfaces, mount table, hostname, users, IPC
cgroupsLimit what the process can consume: CPU, memory, block I/O, PIDs

Nothing is emulated. There is no guest kernel, no virtual CPU, no BIOS. docker run alpine sh starts a process on your kernel that happens to believe it is PID 1 in a small Alpine filesystem.

This is why docker run --rm alpine uname -r prints your host’s kernel version and not Alpine’s. The distribution in the image supplies the userland — libc, shell, package manager, binaries. The kernel is always the host’s.

Two consequences follow directly, and both matter in practice:

  1. You cannot run a Linux container on a Windows kernel, or vice versa. Docker Desktop on macOS and Windows runs a small Linux VM to provide the kernel. The containers are Linux containers; the VM is the compatibility layer.
  2. A kernel bug or kernel-level exploit is shared. Container isolation is weaker than VM isolation, and that trade-off is deliberate.

Topic 3: Containers vs Virtual Machines

The comparison gets repeated so often it turns into slogans. Here is the version worth remembering, because each row has an operational consequence:

Virtual machineContainer
KernelEach VM boots its ownAll containers share the host’s
Start timeTens of seconds to minutesMilliseconds to a second
FootprintGigabytes; full OS installMegabytes; just your app and its libs
DensityA handful on a laptopDozens to hundreds
SnapshotsUsed sparingly, large and opaqueLayers, built incrementally, diffable
VersioningVMX/VMDK files, not really version-controlledImages are tagged, digested, diffed, pushed
Instances per artefactOne VM per set of disk filesMany containers from one image
Isolation strengthStrong — hardware-level boundaryWeaker — a shared kernel boundary

That last row is the one people skip. If you are running untrusted, multi-tenant code, a VM boundary is a genuinely different security proposition. The usual production answer is both: containers inside VMs, which is exactly what every managed Kubernetes service gives you.

The row that changes how you work:

“Images are built incrementally on top of another like layers.” Nothing else in the table changes your daily habits as much. It means a rebuild after a one-line code change transfers a few kilobytes, not a gigabyte, because every layer below your code is already on the target host. Lesson 5 is entirely about exploiting that.


Topic 4: Stateless and Stateful

Docker fits some applications better than others, and the dividing line is where state lives.

Stateless applications keep no local state. Session data, uploads and application data all live in an external system — a database, an object store, a cache. Any instance can serve any request; killing one loses nothing. This is the shape containers were designed for, and it is why containers and horizontal scaling go together so naturally.

Stateful applications keep some or all state on local disk. They run on Docker perfectly well, but every operation gets more careful: you must attach durable storage, you must think about what happens when the container moves to another host, and you must have a backup story. Lesson 8 covers the mechanism (volumes); the discipline is yours.

The honest guidance from people who have run both: prefer stateless in containers, and treat every stateful container as a deliberate decision with a written recovery plan. Not “never run databases in containers” — that advice is dated — but “know exactly which directory holds the data and where it actually lives on the host.”


Topic 5: What Docker Is Bad At

Every tool description that only lists advantages is marketing. The real limitations:

  • Rich GUI applications. Possible with X forwarding and a lot of fiddling; almost never worth it.
  • Managing containers at scale. Docker runs containers on one host. Twenty hosts and two hundred containers is an orchestration problem, and Docker alone does not solve scheduling, self-healing, rolling updates or service discovery across hosts. That is what Kubernetes is for.
  • Cross-platform kernel compatibility. Linux containers need a Linux kernel. Full stop.
  • Backup and disaster recovery. Docker gives you volumes. It does not give you snapshots, retention, or restore testing. That is still your problem.
  • GPU and specialised hardware. Workable, but it needs device plugins and host driver alignment — the container’s portability promise weakens the moment it depends on a specific host driver.

The advantages are real too, and worth stating precisely rather than as adjectives:

  • Rapid deployment — no OS boot in the critical path.
  • No pre-allocation of RAM — a container consumes what the process consumes, unlike a VM’s reserved allocation.
  • Build once, run anywhere the kernel matches — the same image artefact moves from CI to staging to production without rebuilding.
  • Version control for environments — the Dockerfile is reviewed, diffed and tagged like source code.
  • Isolation — a dependency upgrade in one service cannot break another on the same host.

Try it yourself: Run docker run --rm -it ubuntu bash and inside it run ps aux. You will see two or three processes, not the hundred running on your host. Then run ps aux | grep bash on the host — the process is right there, in the host’s list, with a normal PID. One process, two views.

Common mistake: Treating a container as a small VM — SSH-ing in, installing packages by hand, and expecting the changes to survive. They will not. Everything you change at runtime lives in a throwaway layer that is deleted with the container. If a change matters, it belongs in the Dockerfile.