Docker Is Not a VM: Containers Explained from the Linux Kernel Up
Docker is often introduced with a convenient explanation:
"A container is like a lightweight virtual machine."
That analogy is useful for about five minutes.
After that, it starts creating the wrong mental model.
A Docker container does not normally boot its own kernel. It does not need a complete guest operating system. It is fundamentally a set of Linux processes isolated and constrained using kernel features.
A better model is:
Container
=
Normal Linux Processes
+
Namespaces
+
cgroups
+
Filesystem Isolation
+
Container RuntimeUnderstanding that model makes Docker networking, resource limits, process management, security, and container performance much easier to reason about.
What a Virtual Machine Actually Does
A virtual machine virtualizes hardware.
A typical architecture looks like:
Physical Server
↓
Hypervisor
┌────┼────┐
↓ ↓ ↓
VM 1 VM 2 VM 3
↓ ↓ ↓
Guest Guest Guest
Kernel Kernel Kernel
↓ ↓ ↓
Apps Apps AppsEach VM usually runs its own operating-system kernel.
From inside the VM, applications behave as if they have their own machine.
This provides a strong isolation boundary, but it also means each VM carries the overhead of a guest OS and kernel.
What a Container Does Instead
Containers take a different approach.
On Linux, containers share the host kernel:
Host Linux Kernel
┌────────┼────────┐
↓ ↓ ↓
Container A Container B Container C
↓ ↓ ↓
Processes Processes ProcessesThe processes are still managed by the same kernel.
Docker does not need to emulate an entire computer for each container.
Instead, Linux kernel features make processes see isolated environments.
This is why containers can generally start quickly and use fewer resources than full virtual machines.
Start With a Normal Linux Process
Suppose you run:
node server.jsLinux creates a process.
The kernel tracks things such as:
PID
Memory
Open files
Network sockets
User
CPU usage
Filesystem accessNow imagine starting that process while changing what it is allowed to see and consume.
Give it:
Its own process-ID view.
Its own network interfaces.
Its own hostname.
Its own filesystem root.
Limited CPU.
Limited memory.
You are getting close to the foundation of a container.
Docker provides tooling around these Linux primitives so developers do not have to assemble them manually.
Namespaces: Making Processes See Different Worlds
Linux namespaces isolate what a process can see.
Different namespace types isolate different resources.
Important examples include:
PID namespace → process IDs
NET namespace → networking
MNT namespace → mount points
UTS namespace → hostname/domain name
IPC namespace → inter-process communication
USER namespace → user/group IDsNamespaces are a major reason a process inside a container can appear to have its own system.
PID Namespaces
Run:
docker run --rm ubuntu ps auxInside the container, you may see only a small process tree.
The container's main process may appear as PID 1.
But on the host, that same process has another PID among all other host processes.
Conceptually:
Host PID Namespace
PID 1 systemd
PID 820 dockerd
PID 9421 node
↓ namespace view
Container PID Namespace
PID 1 nodeThe process did not become a process on another kernel.
The same host kernel is presenting a different process-ID view.
This detail also explains why PID 1 behavior matters inside containers, particularly around signals and child-process cleanup.
Network Namespaces
Containers can also receive isolated network stacks.
A network namespace can provide its own:
Interfaces
IP addresses
Routing table
Firewall rules
PortsA simplified Docker network might look like:
Container
│ eth0
│
veth pair
│
Docker Bridge
│
Host Network
│
InternetThis is why two containers can both listen on port 3000 internally.
They are listening inside different network namespaces.
Publishing a port:
docker run -p 8080:3000 appconnects host-side traffic on port 8080 to the container's network endpoint on port 3000.
The container does not own a tiny virtual physical machine. It has an isolated network view managed through Linux networking primitives.
Mount Namespaces and Container Filesystems
A process normally sees the host's filesystem hierarchy.
Containers need a different view.
Mount namespaces allow processes to see different filesystem mounts.
Inside a container:
/
├── app
├── bin
├── etc
├── lib
└── tmpThat root filesystem may look like a complete Linux installation, but it does not mean the container is running its own kernel.
It contains user-space files:
Shell
Libraries
Executables
Configuration
Application codeThe kernel is still the host kernel.
This distinction is important:
Container Image
≠
Operating System KernelAn Ubuntu container image provides Ubuntu user-space files. It does not boot an Ubuntu kernel inside the container.
Where Docker Images Fit In
A Docker image provides the filesystem and metadata used to create a container.
Consider:
FROM node:22
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]The resulting image contains layers representing filesystem changes.
Conceptually:
Application Layer
↓
Dependencies Layer
↓
Node Base Layer
↓
Read-Only Image LayersWhen a container starts, the runtime adds a writable container layer on top.
Writable Container Layer
↓
Application Layer
↓
Dependencies Layer
↓
Base ImageThis layered model makes images reusable and efficient.
Multiple containers can use the same underlying image layers while maintaining separate writable state.
Overlay Filesystems
Docker commonly uses a union filesystem such as OverlayFS on Linux.
A simplified view:
Upper Layer
(container writes)
+
Lower Layers
(image)
↓
Merged ViewThe application sees one filesystem even though it is composed from multiple layers.
If the container modifies a file from a read-only image layer, the filesystem can use copy-on-write behavior rather than changing the original image.
This is one reason containers created from the same image remain independent.
cgroups: Controlling What a Container Can Consume
Namespaces answer:
"What can this process see?"
Control groups, or cgroups, answer:
"How much can this process use?"
Without resource limits, a containerized process can potentially compete with other processes for host resources.
Docker can apply limits such as:
docker run --memory=512m appor:
docker run --cpus=1.5 appConceptually:
Host Resources
↓
cgroups
┌────┼────┐
↓ ↓ ↓
App A App B App C
512MB 2GB 1GBModern Linux systems commonly use cgroup v2, which provides a unified hierarchy for controlling and accounting for resources.
This is not hardware virtualization.
The Linux kernel is scheduling and accounting for ordinary processes while enforcing resource policies around them.
Docker Is More Than Namespaces and cgroups
Saying "containers are namespaces plus cgroups" is a useful simplification, but Docker does more than call a couple of kernel APIs.
A modern container stack typically includes multiple layers.
A simplified flow is:
docker CLI
↓
Docker Engine
↓
containerd
↓
OCI Runtime
↓
Linux KernelDocker manages developer-facing concerns such as:
Images.
Builds.
Networks.
Volumes.
Container lifecycle.
Registries.
Lower-level runtimes handle the mechanics of creating the container process according to standardized runtime specifications.
The kernel provides the isolation and resource-control primitives underneath.

What Happens When You Run docker run?
Suppose you execute:
docker run nginxAt a high level:
1. Find / pull image
↓
2. Prepare image filesystem
↓
3. Create container configuration
↓
4. Configure namespaces
↓
5. Configure cgroups
↓
6. Configure networking
↓
7. Start container processAt the end of the chain, the important thing running is still a process.
You can see container processes from the host using normal Linux process tools.
That is the mental model worth remembering.
Containers Do Not Boot Like VMs
A VM usually goes through something resembling:
Virtual Hardware
↓
Bootloader
↓
Guest Kernel
↓
Init System
↓
Services
↓
ApplicationA container can be much simpler:
Container Runtime
↓
Application ProcessFor example:
CMD ["node", "server.js"]The Node process can become the container's PID 1 directly.
There may be no full init system at all.
This contributes to fast container startup.
Why Does a Container Stop When Its Main Process Stops?
Docker associates the lifecycle of a container with its main process.
If the process started as PID 1 exits:
Container PID 1
↓
Exit
↓
Container StopsThis surprises developers who think of a container as a small VM.
Inside a VM, one application exiting does not shut down the entire machine because the operating system and init process continue running.
Inside a typical container, the main application is the primary process the container exists to run.
Why Can Linux Containers Run Different Distributions?
You can run:
docker run ubuntu
docker run alpine
docker run debianon the same Linux host.
How?
Because distributions are not only kernels.
A distribution also includes user-space software:
Package manager
C library
Shell
Utilities
ConfigurationEach image can provide different user-space environments while using the same host Linux kernel.
Conceptually:
Ubuntu User Space ─┐
Alpine User Space ─┼→ Host Linux Kernel
Debian User Space ─┘This also explains an important limitation: containers cannot simply use an incompatible kernel as if they were VMs.
What About Docker on macOS and Windows?
Linux containers depend on Linux kernel features.
macOS does not have the Linux kernel.
So Docker Desktop typically runs Linux containers inside a lightweight Linux virtual machine.
The architecture becomes:
macOS
↓
Lightweight Linux VM
↓
Linux Kernel
↓
Docker ContainersThe containers are still containers.
But a VM exists underneath them to provide the Linux kernel they require.
This is one reason saying "Docker is a VM" is misleading while saying "Docker Desktop may use a VM to run Linux containers on a non-Linux host" is accurate.
Windows also has multiple container modes, including Windows containers that use Windows kernel primitives and Linux containers that require a Linux environment.
Containers vs Virtual Machines
The difference is easiest to see side by side.
Virtual Machines
Hardware
↓
Hypervisor
↓
Guest OS + Kernel
↓
ApplicationContainers
Hardware
↓
Host OS + Kernel
↓
Isolated Processes
↓
ApplicationA practical comparison:
Area Container Virtual Machine
Kernel Shares host kernel Own guest kernel
Isolation Process/kernel boundary Hardware virtualization primitives
Startup Usually fast Usually slower
Size Often smaller Usually larger
OS flexibility Must fit host-kernel model Can run separate guest OS
Resource overhead Lower Higher
Typical unit Application/process Complete machine
Containers and VMs are not mutually exclusive. Production systems frequently run containers inside virtual machines.
Container Isolation Is Not Identical to VM Isolation
Because containers share a kernel, the security boundary is different from full hardware virtualization.
Container security therefore relies on several layers, including:
Namespaces
cgroups
Linux capabilities
seccomp
AppArmor / SELinux
User namespaces
Filesystem permissions
Runtime configurationFor example, Linux capabilities allow a runtime to remove powerful privileges from a process rather than treating root as one all-or-nothing permission set.
A production container should generally receive only the privileges it actually needs.
Running code inside a container does not automatically make unsafe code safe.
Volumes Exist Because Container Filesystems Are Disposable
A container's writable filesystem is usually tied to that container.
If important data exists only there:
Container
↓
Writable Layer
↓
Container Deleted
↓
Data GonePersistent data should live outside the disposable container layer.
Docker volumes provide one option:
Container
↓
Volume
↓
Persistent DataFor example:
docker run \
-v postgres-data:/var/lib/postgresql/data \
postgresThe PostgreSQL process can be replaced while the volume remains.
This follows a useful container principle:
Treat containers as replaceable compute units, not as permanent machines.
Why This Mental Model Helps in Production
Understanding containers as processes explains many common Docker behaviors.
High CPU
The container is not "using CPU" independently of Linux.
Its processes are being scheduled by the host kernel, potentially under cgroup limits.
Out-of-Memory Errors
A process can hit its container memory limit even when the host still has memory available.
Port Conflicts
Containers have network namespaces, while published host ports still need valid host-side mappings.
Files Disappear
The writable container layer is not durable storage.
Signals Matter
Stopping a container sends signals to its main process, so applications should handle termination correctly.
A Container Exits Immediately
Its main process exited.
Once you stop imagining a miniature VM, these behaviors become much less mysterious.
Containers Are an Isolation and Packaging Model
Docker combines two important ideas.
Packaging
An image gives you a reproducible application filesystem:
Application
Dependencies
Runtime
Configuration defaultsIsolation
Linux provides mechanisms that control what the process sees and what resources it can consume.
Together:
Container Image
+
Kernel Isolation
+
Resource Controls
+
Runtime Configuration
↓
ContainerThat combination is why containers work well for deploying applications consistently across environments.
Common Misconceptions
"Every Container Has Its Own OS"
It has its own user-space filesystem view, but Linux containers normally share a kernel.
"A Container Is Just a Small VM"
The developer experience may feel similar, but the underlying isolation model is different.
"Containers Have No Overhead"
They avoid much of the overhead of a guest OS, but networking, filesystem layers, runtime services, logging, and isolation still have costs.
"Containers Are Automatically Secure"
Isolation helps, but permissions, images, capabilities, secrets, kernel security, and runtime configuration still matter.
"One Container Should Run an Entire Server"
Containers are usually easier to operate when centered around a primary responsibility and lifecycle, rather than recreating a traditional multi-service machine inside every container.
The Linux-Kernel View of a Container
If you remember one diagram from this article, make it this:
Linux Kernel
│
┌─────────────────┼─────────────────┐
│ │ │
Namespaces cgroups Security Controls
│ │ │
└─────────────────┼─────────────────┘
↓
Container Process
↓
Application + Libraries
↓
Image FilesystemDocker provides the tooling and lifecycle around this model.
The kernel does the fundamental process isolation and resource enforcement.
Conclusion
Docker containers are not lightweight virtual machines in the technical sense.
A Linux container is better understood as one or more processes running on the host kernel with isolated views of system resources and controlled access to CPU, memory, networking, filesystems, and privileges.
Namespaces create isolation.
cgroups control resources.
Layered filesystems provide the container's filesystem view.
Security mechanisms restrict privileges.
Docker and its runtime stack package these capabilities into a developer-friendly workflow.
Once you understand that a container is fundamentally an isolated process rather than a tiny computer, Docker becomes much easier to reason about—from PID 1 and port mappings to memory limits, volumes, security, and production deployments.
Comments