LevelUpWith

Explore / Docker Containers from First Principles / Lesson 1

Lesson 1: What a Container Really Is

Learn that a container is an ordinary process that shares the host's kernel but is fenced off by namespaces and cgroups, and see how that makes it different from a virtual machine, which runs a whole guest operating system.

Free to read, no account needed. Part of Docker Containers from First Principles.

By the end of this level you'll be able to

  • Explain in plain terms how namespaces (what a process can see) and cgroups (what it can use) isolate a container, and why a container starts in about a second while a VM boots a full OS
  • Compare containers and virtual machines on isolation, start-up time, size and kernel sharing, and pick the right one for a given workload
  • Define image, container, registry and container runtime, and say where each fits in the Docker workflow
  • Run a container next to an ordinary host process, for example with `docker run --rm alpine ps` and `docker run --rm alpine uname -r`, and confirm from the output that the container sees only its own p

Why start with what's under the hood?

People often describe a container as "a lightweight VM". That picture gets you through a demo, and it leads you wrong later: you'll be surprised that a container can't run a Windows kernel on a Linux host, that killing PID 1 stops the whole container, and that a memory limit can get your app killed without warning. So before you write a Dockerfile, it's worth ten minutes on the real model.

A container is an ordinary Linux process. It runs on the host's kernel, just like your editor or your shell. The difference is that the kernel puts two kinds of fence around it:

  • Namespaces control what the process can see.
  • cgroups (control groups) control what the process can use.

That's the whole trick. There's no second operating system and nothing to boot.

Namespaces: what a process can see

A namespace gives a process its own private view of one part of the system. Linux has eight kinds:

Namespace What the container gets its own copy of
PID Process IDs. Its first process is PID 1
Mount The filesystem tree it sees as /
Network Network interfaces, IP addresses and ports
UTS Hostname
IPC Shared memory and message queues
User User and group IDs
Cgroup Its view of the cgroup tree
Time Boot and monotonic clocks

An analogy: think of an open-plan office where each team works behind frosted-glass partitions. Everyone shares the same building, power and air conditioning (the kernel), but each team only sees its own desks and whiteboards (processes, files, network).

cgroups: what a process can use

Namespaces hide things, but they don't stop a process from using all the CPU or memory. That's the job of cgroups, a kernel feature that groups processes together so their CPU, memory and I/O can be measured and limited. The office analogy still works: cgroups are the meter on each team's power supply, plus a breaker that trips if they draw too much.

Gotcha: if a container goes over its cgroup memory limit, the kernel's out-of-memory killer ends the process. From outside, your app just looks as if it vanished. You'll set these limits yourself in a later level.

Containers vs virtual machines

A virtual machine emulates hardware. A hypervisor runs a complete guest operating system, with its own kernel, on top of that virtual hardware. A container skips all of that and shares the host's kernel.

Container Virtual machine
Kernel Shares the host's Has its own guest kernel
Start-up About a second or less (it's starting a process) Tens of seconds or more (boots an OS)
Size Megabytes (just the app and its libraries) Gigabytes (a whole OS)
Isolation Process-level, enforced by one shared kernel Hardware-level, enforced by the hypervisor
Runs a different OS kernel? No Yes

Which should you pick? Use containers to package and ship applications: web services, workers and build tools, where you want quick starts and dense packing. Use a VM when you need a different kernel or OS (say, Windows on a Linux server), or stronger isolation for code you don't trust, because a kernel bug can let a process escape a container in a way a hypervisor would stop. In real systems you'll often have both: cloud container services usually run your containers inside VMs.

Trade-off worth knowing: Docker Desktop on Windows and macOS runs Linux containers inside a small Linux VM, using WSL 2 on Windows. Your containers share that VM's kernel, not the Windows or macOS kernel. You'll see this in the exercise below.

The four words you'll use every day

  • Image: a read-only template containing a filesystem and settings: your app, its libraries and the command to run. Think of it as a class.
  • Container: a running (or stopped) instance of an image, the fenced-off process. Think of it as an object. One image can run as many containers.
  • Registry: a server that stores and hands out images. Docker Hub is the default public one.
  • Container runtime: the software that actually sets up the namespaces and cgroups and starts the process. In Docker, the docker CLI talks to the Docker daemon, which hands the work to containerd, which uses runc (the reference runtime of the Open Container Initiative) to create the container.

The flow goes like this: docker run → the daemon checks for the image locally → if it's missing, pulls it from a registry → the runtime creates namespaces and cgroups → your process starts as a container.

Hands-on: a container next to a host process

If Docker isn't installed yet, read along now and run this after Level 2, which covers installation.

# 1. On the HOST: count how many processes are running (Linux/macOS/WSL shell)
ps aux | wc -l
# typical output: 300 or more. Your machine runs a lot of processes

# 2. In a CONTAINER: list every process it can see
#    --rm deletes the container when it exits; alpine is a ~few-MB Linux image
docker run --rm alpine ps

Expected output (the first run also prints download progress while it pulls alpine):

PID   USER     TIME  COMMAND
    1 root      0:00 ps

That one line is the PID namespace at work. The container sees one process, its own, as PID 1, while the host is running hundreds.

# 3. Compare kernels
uname -r                       # on the host (Linux) or inside WSL
docker run --rm alpine uname -r

On a Linux host, both commands print the same kernel version, which shows the container has no kernel of its own. On Docker Desktop for Windows you'll see something like ...-microsoft-standard-WSL2, the kernel of the Linux VM that Docker Desktop runs, not Windows. Alpine has no kernel of its own: it's a filesystem running on someone else's kernel.

# 4. Bonus: the UTS namespace gives the container its own hostname
hostname                         # your machine's name
docker run --rm alpine hostname  # a random 12-character ID like 3f9c2a7b1e04

Check your result: you've succeeded if the container's ps shows just one or two processes with PID 1 at the top, and its uname -r matches the host's (Linux) or shows a Linux VM kernel (Desktop). Run step 2 again and see how much faster it is: the image is cached, so all that's left is starting a process.

Key takeaways

  • A container is a normal process on the host kernel, fenced off by namespaces (what it sees) and cgroups (what it uses).
  • It starts in about a second because nothing boots. A VM has to boot a whole guest OS.
  • Containers share the kernel: they're small and fast, but you can't run a different kernel and the isolation is weaker than a VM's.
  • Image = template, container = running instance, registry = image store, runtime = what creates the container (containerd + runc in Docker).
  • On Windows and macOS, Docker Desktop runs your containers inside a Linux VM.

A 6-question knowledge check follows this lesson in the app, free and without an account.

More like this

  • HPC on GCP and Azure: Test, Benchmark, Decide

    Take one open benchmark from your laptop through containers, Kubernetes, Terraform, Harness and Backstage to real tests on GKE, Cloud Batch, Slurm, AKS, Azure…

    Course · Cloud · Cloud & Compute · Expert · 60 levels · 4 free · Professionally curated by levelupwith.com

  • Google Cloud Professional Cloud Network Engineer Exam Prep

    Design, build, secure and troubleshoot Google Cloud networks with gcloud and the console, covering VPCs, Shared VPC, Network Connectivity Center, Cloud Load Ba…

    Exam prep · Google · Cloud & Compute · Expert · 58 levels · 4 free · Professionally curated by levelupwith.com

  • Google Cloud Professional Cloud DevOps Engineer Exam Prep

    Prepare for the Professional Cloud DevOps Engineer exam by working through every item in Google's exam guide: bootstrapping an organisation, CI/CD and supply-c…

    Exam prep · Google · Cloud & Compute · Expert · 52 levels · 4 free · Professionally curated by levelupwith.com

  • Google Cloud Professional Cloud Developer Exam Prep

    Prepare for Google Cloud's Professional Cloud Developer exam section by section, with short code and gcloud examples and original scenario questions. You'll le…

    Exam prep · Google · Cloud & Compute · Expert · 50 levels · 4 free · Professionally curated by levelupwith.com

  • Microsoft Azure Network Engineer (AZ-700) Exam Prep

    Learn to design, deploy and troubleshoot every Azure networking skill in Microsoft's AZ-700 study guide (skills measured as of July 27, 2026). This covers core…

    Exam prep · AZ-700 · Cloud & Compute · Expert · 41 levels · 4 free · Professionally curated by levelupwith.com

  • Ship It on AWS: From First Sign-In to Production

    Build, deploy and run one small Python web app with a database on AWS. You'll go from an empty account to a container or Lambda service with S3 uploads, secret…

    Course · AWS · Cloud & Compute · Capable · 20 levels · 4 free · Professionally curated by levelupwith.com