Linux Namespaces & cgroups
Containers are isolated Linux processes. Namespaces isolate what a process can see; cgroups limit what it can use.
Namespaces = blindfolds (can't SEE other processes). cgroups = handcuffs (can't USE more than your share). A container is a blindfolded, handcuffed process — still in the same building (kernel).
Linux namespaces: PID (isolated process tree), network (isolated network stack), mount (isolated filesystem), UTS (isolated hostname), IPC (isolated inter-process communication), user (isolated UID/GID mapping). cgroups (control groups) enforce resource limits: CPU, memory, I/O. A container is a process with all these namespaces applied plus cgroup limits — no hypervisor, no separate kernel.
Each Docker container gets its own network namespace (private eth0, loopback), PID namespace (PID 1 is your entrypoint process), and mount namespace (container's root filesystem is a union mount via overlayfs). The host kernel is shared — 'uname -r' inside a container returns the host kernel version. cgroup v2 (unified hierarchy) is now default in modern Linux. Memory limit enforcement: exceeding the memory cgroup limit causes the OOM killer to terminate the container process (you see OOMKilled in Kubernetes). seccomp profiles and AppArmor/SELinux add syscall-level security on top.
Containers use two Linux kernel primitives: namespaces for isolation and cgroups for resource limits. Namespaces make a process think it's alone — it has its own PIDs, network interfaces, filesystem mounts, and hostname. cgroups enforce hard limits on CPU, memory, and I/O. The container shares the host kernel — there's no hypervisor. This is why containers start in milliseconds vs seconds for VMs, and why a container escape is a kernel vulnerability.
Containers are NOT VMs. They share the host kernel. A container running Linux on a Windows Docker Desktop is running inside a lightweight Linux VM that Docker Desktop manages — the container itself still uses that VM's kernel.