Docker Tutorial — What Are Containers?

4 minute read

A container is a lightweight, isolated environment that packages your application code together with all its dependencies — libraries, runtime, system tools — so it runs the same way everywhere. Think of it as a shipping container for software: standardized, portable, and self-contained.


The Problem: “Works on My Machine”

Every developer has heard (or said) this phrase. The app works perfectly on your laptop, but crashes in staging. Why?

  • Your laptop has Python 3.11, staging has 3.9
  • You installed libssl-dev months ago, the server never got it
  • Your local config points to localhost, production expects a different hostname
  • Different OS versions handle file paths differently

The root cause: environments differ. Your code doesn’t run in isolation — it depends on dozens of system-level components that vary between machines.


What Is a Container?

A container is a process running on your machine — but with isolation. It has:

  1. Its own filesystem — sees only the files packaged inside it
  2. Its own network — gets its own IP address, ports, DNS
  3. Its own process tree — PID 1 inside the container is just a regular process to the host
  4. Resource limits — can be capped on CPU, memory, I/O
+----------------------------------+
|         Your Application         |
|    (code, configs, binaries)     |
+----------------------------------+
|       Runtime & Libraries        |
|    (Python 3.11, Node 20, etc.)  |
+----------------------------------+
|       Base OS Libraries          |
|    (libc, libssl, ca-certs)      |
+----------------------------------+
|     Container Runtime Layer      |
+----------------------------------+
|        Host OS Kernel            |
+----------------------------------+

The key insight: containers share the host kernel but isolate everything else. This makes them much lighter than virtual machines.


How Containers Work: Linux Kernel Features

Containers aren’t magic — they use two Linux kernel features:

Namespaces (Isolation)

Namespaces give each container its own isolated view of system resources:

Namespace What It Isolates
PID Process IDs — container sees its own PID 1
NET Network interfaces, IP addresses, ports
MNT Filesystem mount points
UTS Hostname
IPC Inter-process communication
USER User and group IDs

cgroups (Resource Limits)

Control groups limit how much resource a container can use:

Resource What It Controls
CPU How much CPU time the container gets
Memory Maximum RAM the container can use
I/O Disk read/write bandwidth
Network Network bandwidth limits

The Shipping Container Analogy

Before standardized shipping containers (invented in the 1950s), loading cargo onto ships was chaotic. Every crate was a different size. Dockers had to carefully arrange each piece.

Standardized containers changed everything:

  • Any cargo fits inside the same container shape
  • Any ship, truck, or train can carry the same container
  • Loading/unloading became automated and fast

Software containers work the same way:

  • Any application (Python, Node, Java, Go) fits inside a container
  • Any machine (laptop, server, cloud VM) can run the same container
  • Deployment becomes automated and predictable

A Brief History of Containers

Year Milestone
1979 chroot — first form of filesystem isolation on Unix
2000 FreeBSD Jails — process isolation beyond just filesystem
2004 Solaris Zones — full OS-level virtualization
2006 cgroups added to Linux kernel (by Google engineers)
2008 LXC (Linux Containers) — first complete container solution on Linux
2013 Docker launched — made containers accessible to everyone
2015 OCI founded — industry standards for container images and runtimes
2017 containerd donated to CNCF — Docker’s runtime becomes an industry standard
2020+ Containers become the default deployment model for cloud-native apps

Docker didn’t invent containers. It made them simple to use.


Containers Are Just Processes

This is the most important mental model: a container is a regular Linux process with extra isolation.

# Run an nginx container
$ docker run -d --name web nginx

# On the HOST, find the container's process
$ ps aux | grep nginx
root     12345  0.0  0.1  ... nginx: master process nginx -g daemon off;

# Inside the container, it thinks it's PID 1
$ docker exec web ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY  STAT  COMMAND
root         1  0.0  0.1  ... nginx: master process nginx -g daemon off;

Same process. Two different views. That’s namespaces at work.


Summary

Concept Key Takeaway
Container Isolated process with its own filesystem, network, and process tree
Namespaces Linux kernel feature providing isolation (PID, NET, MNT, etc.)
cgroups Linux kernel feature limiting resources (CPU, memory, I/O)
Shared kernel Containers share the host OS kernel (unlike VMs)
Portability Same container runs identically on any machine with a container runtime
History chroot (1979) → LXC (2008) → Docker (2013) → OCI standards (2015)

Quick Reference

Container = Process + Isolation (namespaces) + Resource Limits (cgroups)

Key Linux features:
  - PID namespace   → isolated process tree
  - NET namespace   → isolated network stack
  - MNT namespace   → isolated filesystem
  - cgroups         → CPU, memory, I/O limits

Container ≠ VM:
  - Containers share the host kernel
  - VMs run their own kernel on a hypervisor

← Introduction Docker vs Virtual Machines →

Updated: