Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Docker Internals Deep Dive
If you love understanding how things actually work, this chapter is for you. If you just want to use Docker and ship code, feel free to skip ahead. No judgment.This chapter pulls back the curtain on Docker. We will explore the Linux kernel features that make containers possible, understand how the Docker daemon orchestrates everything, and demystify the image layer system. This knowledge separates container users from container engineers.
Why Internals Matter
Understanding Docker internals helps you:- Debug production issues when containers misbehave
- Optimize performance by understanding resource allocation
- Ace interviews where internals questions are common
- Make informed decisions about container security
- Build better Dockerfiles by understanding how layers work
The Big Secret: Containers Are Not Virtual Machines
Here is the fundamental truth that changes everything: containers are just isolated Linux processes. There is no hypervisor. There is no guest operating system. Containers share the host kernel and use kernel features to create isolation.- Faster to start (no OS boot, just process fork)
- More lightweight (no duplicate OS, shared kernel)
- Less isolated (shared kernel attack surface)
The Three Pillars of Container Isolation
Docker leverages three key Linux kernel features to create containers:1. Namespaces - What You Can See
Namespaces provide isolation by limiting what a process can see. Each namespace type isolates a different aspect of the system.| Namespace | Flag | What It Isolates |
|---|---|---|
| PID | CLONE_NEWPID | Process IDs - container sees only its own processes |
| NET | CLONE_NEWNET | Network interfaces, IP addresses, routing tables |
| MNT | CLONE_NEWNS | Mount points - container has its own filesystem view |
| UTS | CLONE_NEWUTS | Hostname and domain name |
| IPC | CLONE_NEWIPC | Inter-process communication (shared memory, semaphores) |
| USER | CLONE_NEWUSER | User and group IDs (root in container != root on host) |
| CGROUP | CLONE_NEWCGROUP | Cgroup root directory |
PID Namespace in Action
NET Namespace Deep Dive
Each container gets its own:- Network interfaces (typically
eth0) - IP address
- Routing table
- Firewall rules (iptables)
/proc/netdirectory
docker0 bridge.
2. Cgroups - What You Can Use
Control Groups (cgroups) limit, account, and isolate resource usage. While namespaces control what you can see, cgroups control what you can use.| Resource | Cgroup Controller | What It Controls |
|---|---|---|
| CPU | cpu, cpuset | CPU time, which CPUs to use |
| Memory | memory | RAM limits, swap |
| I/O | blkio | Block device I/O bandwidth |
| Network | net_cls, net_prio | Network traffic classification |
| PIDs | pids | Maximum number of processes |
Setting Resource Limits
What Happens When Limits Are Exceeded
This is critical to understand because the failure modes are completely different depending on the resource:- Memory: The OOM (Out of Memory) killer terminates the process immediately — no graceful shutdown, no cleanup. Your app just disappears. This is the most common cause of mysterious container “crashes” in production.
- CPU: The process is throttled, not killed. It still runs, just slower. Users experience increased latency rather than errors. This is usually the better failure mode, which is why some teams prefer CPU limits over memory limits.
- PIDs: Fork bomb protection —
fork()syscalls return errors. This prevents a runaway process from spawning thousands of children and taking down the host.
3. Union Filesystems - The Layer Cake
Union filesystems allow stacking multiple directories as a single unified view. This is the magic behind Docker’s efficient image layering.How Layers Work
Copy-on-Write (CoW) Strategy
Copy-on-write is a resource-sharing strategy borrowed from operating systems. Think of it like a shared library book — everyone reads the same copy, but if someone wants to write notes in the margins, they photocopy the page first and write on the copy. When a container modifies a file from a lower (read-only) layer:- The file is copied up to the container’s writable layer
- The modification happens on the copy
- The original file in the image layer remains unchanged
- 100 containers from the same image share the base layers — massive space savings; you do not have 100 copies of Ubuntu on disk
- Container startup is instant — no copying the entire filesystem, just adding an empty writable layer on top
- Deleting a file does not reduce image size — a “whiteout” marker is created in the upper layer that hides the file, but the original bytes still exist in the lower layer
Storage Drivers
Docker supports multiple storage drivers, each implementing union filesystem differently:| Driver | Technology | Best For |
|---|---|---|
| overlay2 | OverlayFS | Modern default, most Linux distros |
| aufs | AuFS | Legacy Ubuntu |
| btrfs | Btrfs | Btrfs filesystems |
| zfs | ZFS | ZFS filesystems |
| devicemapper | Device Mapper | RHEL/CentOS legacy |
Docker Daemon Architecture
The Docker daemon (dockerd) is the brain of Docker. Let us trace what happens when you run docker run nginx.
The Request Flow
Component Breakdown
Docker CLI: The command-line interface you interact with. Translates commands into REST API calls. Docker Daemon (dockerd):- Listens on Unix socket
/var/run/docker.sock - Manages images, networks, volumes
- Orchestrates container lifecycle
- Exposes the Docker API
- High-level container runtime
- Manages container lifecycle (create, start, stop)
- Handles image pull/push
- CNCF graduated project (also used by Kubernetes)
- Low-level OCI runtime
- Actually creates and runs containers
- Sets up namespaces and cgroups
- Reference implementation of OCI Runtime Spec
What Happens During docker run nginx
- CLI parses command, sends POST to
/containers/create - dockerd checks if
nginximage exists locally - If not, dockerd tells containerd to pull from registry
- containerd downloads image layers, unpacks them
- dockerd creates container metadata
- containerd calls runc with container spec
- runc creates namespaces, sets up cgroups, mounts filesystem
- runc executes the container’s entrypoint (nginx)
- runc exits, containerd monitors the running container
The OCI Specification
The Open Container Initiative (OCI) defines industry standards for containers:OCI Runtime Spec
Defines how to run a container given a filesystem bundle:config.json- container configurationrootfs/- container root filesystem
OCI Image Spec
Defines the format of container images:- Manifest (list of layers)
- Configuration (env vars, entrypoint)
- Layers (tar.gz of filesystem changes)
Security Implications
Understanding internals reveals security considerations:Shared Kernel Attack Surface
Since containers share the host kernel, a kernel vulnerability affects all containers.Root in Container vs Root on Host
By default, root inside a container is UID 0 — the same as host root. This means a container escape (via kernel exploit, misconfigured mount, or exposed Docker socket) gives the attacker full root access to the host. Think of it like a jail where the inmates have the warden’s keys — the walls only matter if the keys stay outside. Mitigation: User NamespacesCapabilities
Linux capabilities break down root privileges into smaller units. Docker drops many capabilities by default:Seccomp Profiles
Limit which system calls a container can make:Interview Deep Dive Questions
These are the questions that separate junior from senior candidates:What is the difference between a container and a VM?
What is the difference between a container and a VM?
Explain how Docker uses namespaces
Explain how Docker uses namespaces
What is copy-on-write and why does Docker use it?
What is copy-on-write and why does Docker use it?
What happens when you run docker run?
What happens when you run docker run?
Why might running as root in a container be dangerous?
Why might running as root in a container be dangerous?
Explain the difference between overlay2 and aufs
Explain the difference between overlay2 and aufs
Debugging with Internals Knowledge
Inspect Container Namespaces
Examine Cgroups
Trace System Calls
Key Takeaways
- Containers are isolated processes, not VMs - they share the host kernel
- Namespaces provide the “what you can see” isolation
- Cgroups provide the “what you can use” resource limits
- Union filesystems enable efficient layered images with copy-on-write
- Docker is a stack: CLI -> dockerd -> containerd -> runc
- OCI standards ensure portability across container runtimes
- Security requires understanding - shared kernel means shared risk
Interview Deep-Dive
A security team asks you to evaluate whether Docker containers provide sufficient isolation for running untrusted code from third-party vendors. What is your assessment?
A security team asks you to evaluate whether Docker containers provide sufficient isolation for running untrusted code from third-party vendors. What is your assessment?
- My short answer is no — not by default. Containers share the host kernel, and kernel vulnerabilities can lead to container escapes. CVE-2019-5736 (runc vulnerability) demonstrated that a malicious container could overwrite the host runc binary and gain root access to the host. For untrusted code, the shared kernel is an unacceptable attack surface.
- For running untrusted workloads, I would recommend one of three approaches depending on the security requirements. First, gVisor (Google’s user-space kernel) intercepts syscalls and implements a subset of Linux in user space. The untrusted code never touches the real kernel. The trade-off is performance overhead (~5-15% for syscall-heavy workloads) and incomplete syscall coverage. Second, Kata Containers run each container inside a lightweight VM, giving you hardware-level isolation with near-container ergonomics. The trade-off is higher memory usage (each “container” boots a minimal VM kernel) and slower startup. Third, Firecracker (from AWS, powers Lambda) provides microVM isolation with very fast boot times (~125ms) and minimal memory overhead (~5MB per VM).
- If the team insists on standard Docker containers, I would layer defenses: user namespaces (so root in the container maps to a nobody on the host), seccomp profiles that restrict syscalls to a minimal allowlist, AppArmor or SELinux mandatory access controls, dropping all capabilities with
--cap-drop=ALL, and read-only filesystems. But even with all of these, I would not call it “sufficient” for genuinely adversarial code — defense in depth reduces risk but does not eliminate the fundamental shared-kernel exposure.
Explain copy-on-write in Docker, and then tell me why running a database directly on the container's writable layer is a performance disaster.
Explain copy-on-write in Docker, and then tell me why running a database directly on the container's writable layer is a performance disaster.
- Copy-on-write means that when a container needs to modify a file from a read-only image layer, OverlayFS copies the entire file from the lower layer to the container’s writable upper layer before the modification happens. The original file remains unchanged in the lower layer. All subsequent reads and writes for that file go to the upper layer copy.
- For a database like PostgreSQL, the write pattern is catastrophic. A database does random I/O across many data files. Every first write to a data page triggers a copy-up from the lower layer. The copy-up is a synchronous operation that blocks the write. For a busy database, this means hundreds or thousands of copy-ups at startup, each involving a full-file copy (not just the modified page). After the copy-up, subsequent writes to that file are at native speed, but the initial penalty is significant.
- Beyond performance, there is a correctness issue. The container’s writable layer is ephemeral — it vanishes when the container is removed. A
docker rmdestroys the database. Even adocker restartis safe (writable layer survives), butdocker rmfollowed bydocker runstarts with a fresh layer and no data. - The fix is volumes. A named volume or bind mount bypasses the union filesystem entirely. The database reads and writes directly to the volume’s filesystem (ext4, XFS, etc.) with zero copy-on-write overhead. The volume persists independently of the container lifecycle.
- In my experience, this is the single most impactful performance and reliability fix for teams running databases in Docker. I have seen PostgreSQL write throughput improve 3-5x just by switching from container-layer storage to a named volume.
Your organization mounts /var/run/docker.sock into CI/CD containers so they can build Docker images. A security auditor flags this. Explain the risk and propose alternatives.
Your organization mounts /var/run/docker.sock into CI/CD containers so they can build Docker images. A security auditor flags this. Explain the risk and propose alternatives.
- Mounting the Docker socket into a container gives that container full, unrestricted access to the Docker daemon API. The Docker daemon runs as root on the host. This means the CI container can: start new privileged containers, mount the host filesystem, access any container on the host, pull/push any image, and effectively has root access to the entire host machine. If the CI job runs untrusted code (e.g., building a pull request from an external contributor), that code inherits full host root access.
- This is not a theoretical risk. In practice, attackers exploit this by running
docker run -v /:/host --privileged alpine chroot /hostfrom inside the CI container — this gives them a root shell on the host. - Alternatives, in order of preference: First, use Kaniko (Google’s in-container image builder). Kaniko builds Docker images from a Dockerfile entirely in user space, without a Docker daemon. It runs as a regular container and pushes directly to a registry. This is the standard approach for Kubernetes-based CI (Tekton, GitHub Actions on GKE). Second, use BuildKit’s rootless mode, which runs the build daemon as a non-root user. Third, use Podman, which is daemonless and can build images without a socket. Fourth, if you must expose the Docker daemon, use a Docker Socket Proxy (like Tecnativa’s) that restricts API calls — allow image build and push but block container creation, volume mounts, and privileged operations.
- The broader principle: CI/CD pipelines are a high-value target for supply chain attacks. Minimizing privileges in the build environment is not optional — it is a core security requirement.
--privileged flag), which disables most of Docker’s security features. A container escape from the inner Docker daemon can reach the outer Docker daemon. The best dind alternative is running in rootless mode or using sysbox (a container runtime that enables Docker-in-Docker without --privileged). But in most cases, Kaniko or BuildKit is still the preferred approach because it eliminates the daemon entirely.Ready to see these internals in action? Next up: Docker Images where we will build optimized, multi-stage Dockerfiles with layer caching.