Career OS

Docker 01 — What Containers Actually Are

You’re about to spend a whole track packaging splitease-api into a container, so you’d better know what a container is — not the marketing version. Almost every beginner carries one wrong idea (“a container is a lightweight VM”) and it poisons everything they do next. Kill that idea today and the rest of Docker becomes obvious.

The Goal

By the end of this module you can:

  • Explain what problem containers solve, in one sentence, without saying “it works on my machine” as a punchline
  • Separate an image from a container the way you separate a class from an object
  • Describe how isolation actually works — namespaces give a container its own view, cgroups limit what it can use
  • Kill the “a container is a lightweight VM” myth and say precisely why a container starts in a second and a VM in a minute
  • Prove with your own hands that a running container is just a normal Linux process the host kernel is fencing off
  • State the Windows reality: Docker Desktop runs one tiny Linux VM so your Linux containers have a kernel to share

The Lesson

The problem containers solve

Your splitease-api runs perfectly on your laptop. It needs Java 21, a specific Postgres, two environment variables, and a Maven build done in a certain order. You hand it to a teammate, or push it to a server, and it breaks — wrong Java, missing variable, different OS. The classic excuse is “but it works on my machine,” and the classic reply is “then we’ll ship your machine.”

That’s literally what a container is: the app plus everything it needs to run — runtime, libraries, config — sealed into one unit that runs identically on any machine that has Docker. Not “usually the same.” Identical. The whole rest of this track exists to make splitease-api run the same on your Windows laptop, your teammate’s Mac, and a rented Linux server in the cloud.

That’s the packaging half. This module is about the runtime half: what is actually running when a container is “up.”

Image vs container — template vs running instance

This is the distinction beginners blur, and blurring it makes every later command confusing. So nail it now, using something you already know:

Java wordDocker wordWhat it is
ClassImageA read-only template. Built once, stored on disk. Does nothing by itself.
Object (new)ContainerA running (or stopped) instance of an image. You can have many from one image.

An image is a frozen snapshot — your app, its Java runtime, its libraries, baked together. It sits on disk doing nothing, like a .class file. A container is what you get when you run that image: a live process, like calling new on a class. One image, many containers — just like one class, many objects. Run splitease-api’s image three times and you have three independent containers, each with its own memory and state, sharing the one image the way three objects share one class definition.

You’ll go deep on how images are actually built — layers, caching — in Docker 02 — Images and Layers. For now: image = template on disk, container = running instance. Say it until it’s reflex.

The myth you have to kill

Here is the single most expensive misconception in Docker: “a container is a lightweight virtual machine.” It feels true — both isolate apps, both feel like “a little computer.” It is wrong, and the difference is the whole point of containers.

A virtual machine boots a complete, separate operating system — its own kernel, its own everything — on top of a hypervisor that fakes the hardware. Run three apps in three VMs and you’re running three full guest operating systems, each gigabytes in size, each taking a minute to boot, all on top of your real OS.

A container does none of that. It is just a normal process running directly on your host’s operating system, like Chrome or your IDE — except the kernel has been told to give that process a fenced-off view of the world. There is no guest OS inside a container. No second kernel. Just your app’s process, isolated.

flowchart TD
  subgraph VM["Virtual Machines"]
    A1["App A"] --> G1["Guest OS"]
    A2["App B"] --> G2["Guest OS"]
    G1 --> HV["Hypervisor"]
    G2 --> HV
    HV --> HOS1["Host OS"]
    HOS1 --> HW1["Hardware"]
  end
  subgraph CT["Containers"]
    C1["App A process"] --> K["Shared Host Kernel"]
    C2["App B process"] --> K
    K --> HW2["Hardware"]
  end

Look at what the VM column duplicates: a whole Guest OS per app. The container column has none of that — every container shares the one host kernel. That single difference is why containers are MB-sized and start in a second, while VMs are GB-sized and boot in a minute. Step through the visualizer and watch exactly what each stack ships and what it skips.

So if there’s no guest OS and the container is “just a process,” what stops it from seeing your other processes, your files, your network? Two Linux kernel features. This is where the real understanding lives.

How isolation actually works — namespaces and cgroups

A container is a process the kernel fences off using two mechanisms. They do different jobs — beginners conflate them, so keep them separate:

Namespaces — what a process can SEE. A namespace gives a process its own private view of one kind of system resource. The Linux kernel has several; the ones that matter to you:

NamespaceThe lie it tells the process
PID”You are process 1. There are no other processes.” (It can’t see the host’s.)
Mount”This filesystem is the whole filesystem.” (Its own root, not yours.)
Network”You have your own network interfaces and ports.”
UTS”Your hostname is a1b2c3, not the host’s name.”

Inside a container, run ps aux and you see only the container’s own processes — usually just a handful — even though the host might be running 300. That’s the PID namespace doing its job: same kernel, different view. The processes are really there on the host (you could see them from outside, with their real host PIDs), but inside, the container thinks it’s alone. That’s the trick — isolation is a lie the kernel tells the process, not a wall around a separate machine.

Cgroups — how much a process can USE. Control groups (cgroups) cap resource consumption: this container gets at most 512 MB of RAM and half a CPU core. Namespaces decide what you see; cgroups decide what you get. Without cgroups, one runaway container could eat all the host’s memory and take down every other container on the box — exactly the kind of “one bad tenant kills the building” problem cgroups exist to prevent.

The one-line memory aid you should never lose: namespaces isolate the view, cgroups limit the resources. Both are features of your one shared kernel — which is the next, crucial point.

”Shares the host kernel” — why containers are cheap

Every container on a machine uses the same single kernel — the host’s. The kernel is the core of an OS: it manages processes, memory, files, and hardware. A VM ships its own kernel; a container borrows the host’s.

That one fact explains every “containers are lightweight” claim you’ll ever read:

Virtual MachineContainer
ContainsA full guest OS + kernelJust your app + its libraries
SizeGigabytesMegabytes
StartupBoots an OS — ~minuteStarts a process — ~second
KernelIts ownShares the host’s
Isolation strengthStronger (full OS boundary)Lighter (kernel-enforced, shared kernel)

A container is small because it doesn’t carry an OS — it borrows yours. It starts fast because starting it is just starting a process, not booting a computer. When you run 20 microservices, 20 containers share one kernel and barely strain the machine; 20 VMs would mean 20 guest OSes and a melted laptop. That is why the industry containerized everything.

The honest trade-off, so you can answer it in an interview: because containers share the host kernel, the isolation is lighter than a VM’s full-OS boundary. For running your own services, that’s a fine trade. For running untrusted code from strangers, the stronger VM boundary still matters — which is why some cloud platforms run each customer’s containers inside a lightweight VM. You don’t need that nuance to ship SplitEase; you need to know it exists.

The Windows reality (read this once, then stop worrying)

Here’s a fair question: namespaces and cgroups are Linux kernel features. You’re on Windows 11. So how does Docker Desktop run Linux containers?

It cheats, cleanly: Docker Desktop runs one tiny, shared Linux VM via WSL2, and your Linux containers run as processes inside that Linux kernel. So there is a VM in the picture on Windows — but exactly one, lightweight, shared by all your containers. The containers themselves are still not VMs; they’re still just isolated processes — they simply borrow the WSL2 Linux kernel instead of a native one.

Practically, this is invisible: you type docker run and it works. Just don’t let the WSL2 VM confuse you into thinking each container is a VM. One Linux VM, many container-processes inside it. Move on.

Build This

Goal: prove with your own eyes that a container is just an isolated process — its own view of processes and hostname, on your machine’s shared kernel. You only need Docker Desktop running.

  1. Confirm Docker is alive. Open PowerShell and run:
docker version

You should see both a Client and a Server section. If the Server section is missing or you get an error, Docker Desktop isn’t running — open it from the Start menu, wait for the whale icon to settle, and retry.

  1. First, look at your host. Note roughly how busy your real machine is and what it’s called:
hostname
docker ps

hostname prints your laptop’s name. docker ps lists running containers — probably none yet. Good baseline.

  1. Run a container and step inside it. This pulls a tiny (~5 MB) Alpine Linux image and drops you into a shell inside the container:
docker run --rm -it alpine sh

Your prompt changes to something like / #. You are now a shell process running inside the container. Decode the flags: --rm deletes the container when you exit (no litter), -it gives you an interactive terminal, alpine is the image, sh is the command to run inside it.

  1. Ask the container who’s running. Inside that shell:
ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps aux

Stop and absorb this. Two processes. Two. Your real machine is running hundreds, but this container sees only sh (as PID 1 — it thinks it’s the first process on a fresh system) and the ps command you just ran. That’s the PID namespace: the kernel handed this process a private view where it’s alone. The host’s processes are still running on the same kernel — the container just can’t see them.

  1. Ask the container its name:
hostname

It prints a short random string like 3f9a2b1c4d5enot your laptop’s name from step 2. That’s the UTS namespace giving it its own hostname. Different identity, same kernel underneath.

  1. Leave:
exit

You’re back to your normal PowerShell prompt. Run docker ps again — the container is gone (--rm cleaned it up).

Definition of done: you can explain, in your own words, why ps aux inside the container showed only two processes while your host runs hundreds — and that this was the same kernel showing the process a different view, not a separate machine. If you can say “namespaces gave it an isolated view, the kernel was shared the whole time,” you’ve got the mental model the rest of the track depends on.

Where to Practice

ResourceWhat to do thereHow long
Play with Docker (labs.play-with-docker.com)Free in-browser Docker, no install. Repeat the Build This alpine sh + ps aux experiment in a clean cloud node — prove it’s the model, not your laptop20 min
Docker Get Started (docs.docker.com/get-started)Read “What is a container?” and “What is an image?” — then close the tab and re-explain image-vs-container out loud before peeking20 min
Your own machineRun docker run --rm -it alpine sh three times in three terminals at once — three containers, one image, one kernel. Watch them coexist10 min

How to Practice

Don’t re-read — re-derive. Once a day for a few days, open a terminal and run the alpine sh + ps aux + hostname drill cold, narrating out loud what each output proves (PID namespace, UTS namespace, shared kernel) before you look. Then explain image-vs-container to an empty chair using the class-vs-object analogy. The model sticks when you can produce it from a blank prompt, not when you recognize it on a page.

Check Yourself

  1. In one sentence, what problem does a container solve?
  2. What’s the difference between an image and a container? Map both to a Java concept.
  3. A container is “just a process.” A process running where, exactly?
  4. What does a namespace do, and what does a cgroup do? Don’t mix them up.
  5. Inside a container, ps aux shows 2 processes while the host runs 300. Which namespace is responsible, and are the host’s processes actually gone?
  6. Why is a container megabytes and a VM gigabytes? Why does one start in a second and the other in a minute?
  7. “A container is a lightweight VM.” What’s wrong with that sentence?
  8. You’re on Windows. Where does the Linux kernel your container shares actually come from?
Answers
  1. It packages an app with everything it needs to run so it runs identically on any machine with Docker — killing “it works on my machine.”

  2. An image is a read-only template on disk (like a class); a container is a running instance of that image (like an object from new). One image → many containers, one class → many objects.

  3. Directly on the host’s operating system / kernel — the same place Chrome and your IDE run. It’s a normal process the kernel has fenced off, not a separate computer.

  4. Namespaces isolate what a process can see (its own processes, filesystem, network, hostname). Cgroups limit what a process can use (CPU, memory). View vs resources.

  5. The PID namespace. The host’s processes are not gone — they’re still running on the same shared kernel. The container is simply given a private view where it can’t see them.

  6. A container ships only the app + its libraries and borrows the host kernel (MB, ~second to start a process). A VM ships a full guest OS + its own kernel (GB, ~minute to boot an OS).

  7. There’s no guest OS or second kernel inside a container. It’s an isolated process sharing the host kernel, not a virtualized machine. The VM mental model makes you misjudge size, speed, and how isolation works.

  8. From the single tiny Linux VM that Docker Desktop runs via WSL2. Your containers are processes inside that shared Linux kernel — still not VMs themselves.

Explain it out loud: Tell an empty chair why running ps aux inside a container shows only its own processes, going all the way down to “same kernel, different view.” If you reach for the word “VM” anywhere in that explanation, you haven’t killed the myth yet — go back to the visualizer.

Still Unclear?

Explain the difference between a container and a VM to me as if I just said
"a container is a lightweight VM" and you need to correct me. Use the shared
kernel as the central idea. Then quiz me with 3 scenarios where I have to
say whether the right tool is a container or a VM and why.
I ran `ps aux` inside an alpine container and saw only 2 processes, but my
host runs hundreds. Explain step by step what the PID namespace did at the
kernel level, where the host's processes actually are, and how I could see
this same process from OUTSIDE the container with a different PID.
I'm on Windows 11 with Docker Desktop and WSL2. Trace exactly what happens
when I run `docker run alpine` — where the Linux kernel comes from, what the
WSL2 VM is doing, and why my container is still NOT a VM even though there's
a VM involved. Don't oversimplify; I want the real plumbing.

Why AI Can’t Do This For You

AI can recite “namespaces and cgroups” flawlessly — but it can’t run ps aux inside a container on your machine and feel the small shock of seeing only two processes where your host runs hundreds. That moment is what converts a memorized definition into a model you actually believe, and you only get it by typing the commands yourself.

And the payoff is judgment AI can’t supply: when a container behaves strangely in production — sees the wrong files, can’t reach a port, gets OOM-killed — the engineer who knows it’s a fenced-off process sharing one kernel debugs in minutes by asking “which namespace, which cgroup limit.” The one who still pictures a magic mini-VM is lost. You can’t prompt your way to that instinct; you build it now, with alpine sh.

Module done? Add it to today’s tracker

Saves your progress on this device.