DEVOPS 12 min

What is Docker: Architecture, Namespaces, and Container Security

Deep technical guide to Docker architecture: how Linux namespaces, cgroups v2, and Overlay2 operate, VM vs container threat models, and the 5 foundational pillars of container security in production.

Written by Samir El Azem Crovetto • Senior Backend Engineer & DevSecOps

Demystifying Containers: What Docker Actually Is

For years, the standard introductory analogy for Docker has been describing it as "a lightweight virtual machine". While convenient as a high-level marketing concept, this metaphor is technically inaccurate and obscures how containers actually operate inside modern operating systems.

A Docker container is not a virtual machine. It does not emulate hardware, run an independent operating system kernel, or require a hypervisor (such as KVM, ESXi, or Hyper-V) to trap and translate CPU instructions.

A container is fundamentally a standard Linux user-space process isolated by kernel-level primitives. It executes directly on the host system's CPU and shares the host kernel, but operates within a strictly restricted view of process trees, networking stacks, filesystem mount points, and resource quotas.

Docker revolutionized software engineering not because it invented isolation primitives—mechanisms like chroot existed in Unix since 1979, followed by FreeBSD Jails in 2000 and Solaris Zones in 2004—but because it packaged these complex kernel subsystems into a unified, reproducible, and immutable standard: the OCI container image and container runtime lifecycle governed today by the Open Container Initiative.


The Three Pillars of the Linux Kernel: Namespaces, cgroups v2, and Overlay2

To understand how Docker isolates and executes software, we must look into the Linux kernel and examine the three foundational technologies underpinning modern containerization:

flowchart TD
    subgraph Docker_Engine ["Docker Engine & Runtime OCI"]
        CLI[Docker CLI / API Client] -->|Unix Socket / HTTP| D[Docker Daemon - dockerd]
        D -->|gRPC| C[containerd]
        C -->|shim| R[runc - OCI Reference Runtime]
    end

    subgraph Linux_Kernel ["Linux Kernel (Host)"]
        R -->|Visibility Isolation| NS[Linux Namespaces<br/>PID, NET, MNT, IPC, UTS, USER]
        R -->|Resource Governance| CG[cgroups v2<br/>CPU, Memory, I/O, PIDs]
        R -->|Syscall Filtering| SC[seccomp & AppArmor<br/>System Call Filters]
    end

    subgraph Storage ["Disk Storage"]
        R -->|Copy-on-Write| OV[Overlay2 Storage Driver<br/>lowerdir + upperdir + merged]
    end

    classDef engine fill:#1e1e2e,stroke:#6366f1,stroke-width:2px,color:#cdd6f4;
    classDef kernel fill:#181825,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4;
    classDef storage fill:#181825,stroke:#f9e2af,stroke-width:2px,color:#cdd6f4;
    class Docker_Engine engine;
    class Linux_Kernel kernel;
    class Storage storage;

1. Linux Namespaces: The Illusion of Visibility Isolation

Namespaces partition global kernel resources so that each container process perceives its own dedicated, isolated operating environment:

  • PID Namespace (Process Tree): The container's primary application process is assigned PID 1 inside its namespace (behaving as the container's init process). However, on the host system, the exact same process holds a standard operating system PID (e.g., PID 48291). The container cannot view, signal, or interact with processes residing outside its namespace boundary.
  • NET Namespace (Networking): Equips the container with an independent network stack, including virtual network interfaces (veth), routing tables, firewall filtering rules (iptables/nftables), and bindable port spaces.
  • MNT Namespace (Mount Points): Isolates filesystem mount tables, allowing the container to mount its own root filesystem (/) without touching or inspecting the host storage tree.
  • IPC Namespace (Inter-Process Communication): Restricts access to POSIX/SysV message queues, semaphores, and shared memory segments between containers.
  • UTS Namespace (Hostname & Domain): Enables each container to define its own hostname and domain without changing host-level configurations.
  • USER Namespace (User & Group Mappings): Maps user (UID) and group (GID) IDs within the container to completely unprivileged IDs on the host. This represents a foundational security mechanism: a process operating as root (UID 0) inside the container is mapped to an unprivileged account (such as UID 10001) in the host kernel.
  • CGROUP Namespace: Masks the host's root cgroup hierarchy to prevent information leakage regarding host hardware topology.

2. Control Groups (cgroups v2): Hardware Resource Governance

While namespaces govern what a process can see, Control Groups enforce how much hardware it can consume:

  • Memory Boundaries (memory.max and memory.high): Prevents noisy neighbors from exhausting host physical RAM. If a container breaches its hard memory boundary, the kernel invokes the Out of Memory (OOM) Killer to terminate that specific process without impacting neighboring services.
  • CPU Quotas (cpu.max): Restricts CPU bandwidth using the Completely Fair Scheduler (CFS), ensuring a runaway computation loop in one container cannot starve host processing capacity.
  • I/O Bandwidth (io.max): Regulates disk read and write throughput to prevent storage bottlenecks.
  • Process Concurrency Limits (pids.max): Defends against Fork Bomb Denial of Service attacks by capping the maximum number of child processes a container can spawn.

3. Overlay2: Layered Filesystems and Copy-on-Write (CoW)

Container images are not monolithic disk blobs; they are ordered stacks of immutable, cryptographically hashed (SHA-256) read-only layers.

The Overlay2 storage driver merges these layers into a unified view:

  1. lowerdir (Image Layers): Static, read-only layers containing the base OS packages, dependencies, and compiled application binaries. These layers are shared across containers instantiated from the same base image.
  2. upperdir (Container Read-Write Layer): A thin ephemeral layer created when the container starts. Any runtime file creations, modifications, or log writes land strictly in this directory.
  3. merged (Unified Mount): The composite mount view presented to the application as a normal filesystem.
  4. Copy-on-Write (CoW): When an application modifies a file originating from a lower image layer, Overlay2 copies the file up to the upperdir before executing write operations, preserving image immutability.

Containers vs. Virtual Machines: The Threat Model

Evaluating containers against hypervisors is not merely a question of boot speed or operational density; it establishes the isolation boundary and threat model of the platform:

Evaluation Dimension Virtual Machines (KVM / VMware ESXi) Docker / OCI Containers
Kernel Model Dedicated, independent kernel per VM instance Shared kernel running on the host OS
Isolation Boundary Hardware-assisted CPU virtualization (VT-x, AMD-V) Software-enforced kernel primitives (Namespaces + cgroups)
Startup Latency 30 to 120 seconds (BIOS, bootloader, kernel, init) 10 to 500 milliseconds (direct process execution)
Resource Overhead High: Gigabytes reserved per Guest OS Near zero: strictly consumes application process memory
Blast Radius of Escape Attacker remains confined within hypervisor boundary Host kernel vulnerabilities (Kernel LPE) compromise the entire host

The Shared Kernel Security Paradigm

Because all containers hosted on a physical node share a single operating system kernel, containers do not provide the same hard security boundaries as hardware-isolated virtual machines.

If an attacker executes malicious code inside a container and exploits a vulnerability within a Linux kernel system call (such as historical flaws in netfilter, io_uring, or eBPF), they can achieve a container breakout (container escape) and gain root control over the entire underlying host.

Consequently, in production enterprise environments and DevSecOps pipelines, containers must be operated under strict Zero Trust assumptions—assuming the process will be compromised and stripping every unnecessary privilege in advance.


The 5 Pillars of Container Hardening for Production

To address the inherent risks of shared-kernel architectures, container engineering must enforce five essential security controls:

1. Mandatory Non-Root Execution

By default, if no USER directive is declared in a Dockerfile, the containerized process executes as UID 0 (root). In the absence of user namespace remapping, a container breakout hands the attacker unrestricted root capabilities over the underlying host.

# Create dedicated unprivileged system user and group
RUN groupadd -g 10001 appgroup && \
    useradd -u 10001 -g appgroup -s /sbin/nologin -d /app appuser

USER 10001:10001

2. Distroless and Minimal Base Images

Over 90% of vulnerabilities reported by vulnerability scanners against standard base images (such as ubuntu:latest or node:20) originate from ancillary OS packages: package managers (apt, apk), interactive shells (/bin/sh, /bin/bash), and network tools (curl, wget, netcat).

Distroless images (maintained by Google and Chainguard) strip away shells and package managers entirely, leaving only the minimal runtime needed to execute application binaries. This deprives attackers of essential exploitation primitives required to stage reverse shells or download external malware.

3. Dropping Unnecessary Linux Capabilities

Linux subdivides traditional superuser power into distinct privileges called Capabilities. By default, Docker retains capabilities that standard microservices never require (such as CAPCHOWN, CAPFOWNER, or CAPNETRAW).

Production deployments should drop all default capabilities and re-enable strictly what is necessary:

# Drop all capabilities, granting strictly unprivileged network binding
docker run -d \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --name api-service \
  my-app:1.0

4. System Call Filtering with Seccomp

The Linux kernel exposes over 450 distinct system calls (syscalls). Docker applies a default seccomp-bpf profile that restricts approximately 44 high-risk calls (such as reboot, ptrace, and sys_chroot).

In high-assurance environments, generating tailored seccomp profiles confines containerized binaries strictly to the specific system calls exercised during normal runtime execution.

5. Read-Only Root Filesystems (read-only rootfs)

Except for explicit data mounts, the root filesystem of a container should be mounted read-only. This prevents adversaries from persisting malicious binaries or dropper scripts into /tmp or /var/tmp:

docker run -d \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  my-app:1.0

Practical Implementation: Production-Grade Multi-Stage Dockerfile

Below is an enterprise production Dockerfile for a Node.js/TypeScript application leveraging multi-stage compilation, distroless runtimes, unprivileged execution, and layer caching:

# ==========================================
# STAGE 1: Build & Compilation
# ==========================================
FROM node:22-bookworm-slim AS builder

WORKDIR /usr/src/app

# Leverage layer caching for dependencies
COPY package*.json tsconfig.json ./
RUN npm ci --include=dev

# Copy application source and compile TypeScript
COPY src/ ./src
RUN npm run build

# Strip devDependencies to retain production packages only
RUN npm prune --omit=dev

# ==========================================
# STAGE 2: Hardened Production Runtime
# ==========================================
# Use Google Distroless runtime for Node.js
FROM gcr.io/distroless/nodejs22-debian12:nonroot

WORKDIR /app

# Production environment variables
ENV NODE_ENV=production
ENV PORT=3000

# Copy compiled artifacts from builder stage
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/package.json ./package.json

# Distroless nonroot user UID 65532
USER nonroot

EXPOSE 3000

# Deterministic exec-form entrypoint without intermediate shell
CMD ["dist/server.js"]

Key Architectural Benefits:

  1. Dramatic Image Size Reduction: Drops from ~1.2 GB down to under 150 MB by omitting the TypeScript compiler, type definition files, and system utilities.
  2. Minimized Vulnerability Surface: Free of package managers and shell interpreters, static scanners report zero critical CVEs.
  3. Execution Tamper Resistance: Attackers cannot invoke shell commands or execute lateral pivoting scripts.

The OCI Ecosystem: From Docker to Podman and Kubernetes

Container technology has transitioned from proprietary implementations to open standards steered by the Open Container Initiative (OCI):

  • OCI Image Specification: Formats manifests, compressed tar archives, and SHA-256 layer hashes into universal distributable artifacts.
  • OCI Runtime Specification: Defines low-level kernel execution contracts, with runc serving as the canonical reference implementation.
  • containerd & CRI-O: High-performance container runtimes engineered specifically for Kubernetes clusters via the Container Runtime Interface (CRI), bypassing legacy Docker daemon overhead.
  • Podman: A daemonless, rootless container engine developed within the Red Hat ecosystem. Podman runs containers as direct process forks of the calling user, eliminating monolithic daemon vulnerabilities and supporting rootless operations out of the box.

Conclusion

Mastering Docker and container architectures in modern engineering requires looking beyond basic CLI commands. It requires understanding the kernel subsystems enforcing isolation, honoring the separation between compilation and runtime environments, and baking defense-in-depth security into every layer of the Dockerfile.

Containers represent the foundational compute abstraction of cloud-native infrastructure. Complementing containerization with automated DevSecOps Guardrails, rigorous Host and Server Hardening, and identity-aware OAuth 2.1 & OIDC Access Controls ensures end-to-end platform resilience.

If your engineering organization is looking to audit container images, deploy hardened distroless workloads, or secure Kubernetes delivery pipelines, explore my Cybersecurity Consulting Services or reach out directly on my About Me profile.