Skip to content

Architecture

Docker uses a layered architecture with a client-daemon separation. The Docker CLI communicates with the Docker daemon (dockerd), which delegates container lifecycle management to containerd, which in turn calls low-level runtimes like runc. Image building is handled by BuildKit.

See also: infrastructure/docker/index, infrastructure/docker/explanation, infrastructure/docker/how-to-guides

Component Overview

graph TD
    CLI["docker CLI"] -->|REST API / Unix socket| DAEMON["dockerd"]
    DAEMON -->|gRPC| CONTAINERD["containerd"]
    CONTAINERD -->|OCI runtime| RUNTIME["runc / runhcs"]
    DAEMON -->|build API| BUILDKIT["BuildKit"]
    BUILDKIT -->|pull layers| REGISTRY["Container Registry"]
    CONTAINERD -->|snapshots| STORAGE["overlay2 / containerd image store"]
    DAEMON -->|manage| NETWORK["Network Drivers"]
    DAEMON -->|manage| VOLUME["Volume Drivers"]

Core Components

dockerd (Docker Daemon)

The Docker daemon (dockerd) is the central management process. It listens for Docker API requests on a Unix socket (/var/run/docker.sock) or TCP port. Key responsibilities:

  • Manage Docker objects: images, containers, networks, and volumes
  • Communicate with other daemons to manage Docker services (Swarm)
  • Delegate container lifecycle to containerd
  • Handle image building via BuildKit integration
  • Serve the Docker Engine API (REST)

Configuration

The daemon is configured via /etc/docker/daemon.json. Common settings include storage-driver, log-driver, registry-mirrors, and userns-remap.

containerd

containerd is an industry-standard container runtime embedded within Docker. It handles the low-level container lifecycle:

  • Pull and push container images
  • Manage image snapshots and metadata
  • Create and supervise containers via OCI runtimes
  • Manage task execution and process monitoring
  • Expose a gRPC API for higher-level tools

Starting with Docker Engine 29.0, the containerd image store is the default storage backend on fresh installations. It replaces the legacy graph drivers. This uses snapshotters instead of classic storage drivers.

runc

runc is the reference implementation of the OCI runtime specification. It is the lowest layer that actually creates and runs containers:

  • Spawn containers from OCI bundles
  • Configure namespaces (PID, network, mount, UTS, IPC, user)
  • Configure cgroups for resource limits
  • Execute the container process

Docker can also use alternative OCI runtimes (for example, kata-containers for VM-based isolation, runhcs on Windows).

BuildKit

BuildKit is the next-generation build engine and replaces the legacy Docker builder. It provides:

  • Parallel build execution of independent stages
  • Better caching mechanisms (cache mounts, inline cache export)
  • Multi-platform builds via docker buildx
  • Support for alternative frontends (for example, Buildkit-specific Dockerfiles)
  • Secrets and SSH forwarding during builds

BuildKit

BuildKit is the default builder since Docker Engine 23.0. It is activated via the docker buildx CLI or automatically by docker build.

Image Layer System and overlay2

Docker images are composed of read-only layers stacked on top of each other. Each instruction in a Dockerfile creates a new layer. When a container starts, Docker adds a thin writable layer on top.

graph BT
    BASE["Base Image Layer<br/>(for example, ubuntu:22.04)"] --> L1["Layer 1: apt-get install"]
    L1 --> L2["Layer 2: COPY app /app"]
    L2 --> L3["Layer 3: RUN build"]
    L3 --> WRITABLE["Container Writable Layer<br/>(thin, ephemeral)"]
    style WRITABLE fill:#f9f,stroke:#333
    style BASE fill:#bbf,stroke:#333

overlay2 Storage Driver

overlay2 is the preferred and default storage driver for all supported Linux distributions. It uses the OverlayFS kernel filesystem:

  • Lower directories: Read-only image layers, stacked from base to top
  • Upper directory: The container writable layer (thin RW layer)
  • Merged directory: The unified view presented to the container
  • Work directory: Used by OverlayFS for internal operations

Layers are stored under /var/lib/docker/overlay2/, with each layer in its own directory. The l subdirectory contains shortened symbolic links to layer directories for performance.

Containerd Image Store

Docker Engine 29.0+ uses the containerd image store by default on fresh installs, which replaces overlay2 graph drivers with containerd snapshotters. Upgraded installations retain overlay2 until explicitly migrated.

Layer Sharing and Efficiency

  • Layers are content-addressed by SHA256 digest
  • Identical layers are shared across images (deduplication)
  • Pulling a new image only downloads missing layers
  • docker image history shows the layer stack for any image

Networking

Docker provides several network drivers for container connectivity:

Bridge (default)

  • Creates a private internal network on the host (docker0 bridge)
  • Containers get private IP addresses from an internal subnet
  • Port mapping (-p host:container) exposes services externally
  • User-defined bridges enable automatic DNS resolution between containers
  • The default bridge does NOT support automatic DNS (containers communicate via IP only)

Host

  • Removes network isolation between container and host
  • Container shares the host network stack directly
  • No port mapping needed. Services bind directly to host interfaces
  • Use case: high-performance networking where NAT overhead is unacceptable

Overlay

  • Connects multiple Docker daemons across different hosts (Swarm mode)
  • Enables container-to-container communication across hosts without OS-level routing
  • Built on top of VXLAN or IPSec encryption
  • Requires a key-value store (or Swarm raft consensus) for network state

Macvlan

  • Assigns a real MAC address to each container
  • Containers appear as physical devices on the network
  • Traffic is routed by MAC address. This bypasses the Docker bridge
  • Supports 802.1Q VLAN trunking via sub-interfaces (for example, eth0.50)
  • Use case: legacy applications expecting direct network presence

IPvlan

  • Similar to macvlan but shares the host MAC address
  • Each container gets its own IP address
  • L2 mode (same subnet) or L3 mode (routed between subnets)
  • Useful when switch port security limits MAC addresses

None

  • Disables all networking for the container
  • Only the loopback interface is available
  • Use case: isolated batch jobs, security-sensitive workloads

Volume Drivers and Storage

Docker volumes provide persistent data storage that outlives container lifecycle:

Type Command Scope
Volume -v myvol:/data Managed by Docker, stored in /var/lib/docker/volumes/
Bind mount -v /host/path:/data Maps directly to host filesystem path
tmpfs --tmpfs /data In-memory only, non-persistent
Named pipe (Windows) Named pipe on Windows hosts

Volume drivers extend Docker to store data on remote hosts, cloud providers, or other storage backends. Third-party plugins support NFS, AWS EFS, Azure File Storage, and more.

Data Safety

Data in volumes persists independently of containers. Removing a container does NOT remove its volumes. Use docker volume prune to clean unused volumes.

Inter-Component Communication

sequenceDiagram
    participant User
    participant CLI as docker CLI
    participant Daemon as dockerd
    participant BuildKit as BuildKit
    participant containerd as containerd
    participant runc as runc
    participant Registry as Registry

    Note over User,Registry: Image Build Flow
    User->>CLI: docker build -t myapp .
    CLI->>Daemon: POST /build
    Daemon->>BuildKit: build request
    BuildKit->>Registry: pull base image layers
    BuildKit->>BuildKit: execute Dockerfile stages
    BuildKit->>containerd: store image layers
    containerd-->>Daemon: image stored
    Daemon-->>CLI: build complete
    CLI-->>User: image ID

    Note over User,Registry: Container Run Flow
    User->>CLI: docker run myapp
    CLI->>Daemon: POST /containers/create
    Daemon->>containerd: create container
    containerd->>containerd: prepare snapshot (overlay2)
    containerd->>runc: create OCI bundle + run
    runc->>runc: setup namespaces + cgroups
    runc-->>containerd: container started
    containerd-->>Daemon: container running
    Daemon-->>CLI: container ID
    CLI-->>User: output

Key File Locations

Path Purpose
/var/run/docker.sock Docker daemon Unix socket
/etc/docker/daemon.json Daemon configuration file
/var/lib/docker/overlay2/ Image and container layers (overlay2 driver)
/var/lib/docker/volumes/ Named volumes
/var/lib/docker/network/ Network configuration and state
/var/lib/containerd/ containerd data (when using containerd image store)
~/.docker/config.json User-level CLI configuration

References


How It Works

Core mechanisms, container lifecycle, image layer system, and networking internals.

Container Lifecycle

stateDiagram-v2
    [*] --> Created: docker create
    Created --> Running: docker start
    Running --> Paused: docker pause
    Paused --> Running: docker unpause
    Running --> Stopped: docker stop (SIGTERM → SIGKILL)
    Stopped --> Running: docker start
    Stopped --> Removed: docker rm
    Running --> Removed: docker rm -f
    Removed --> [*]
    Running --> Restarting: crash / restart policy
    Restarting --> Running: restart

Image Layer System

Docker images use a Union File System (overlay2 by default) that stacks read-only layers. Each Dockerfile instruction creates a new layer. When a container runs, a thin writable layer is added on top.

flowchart TB
    subgraph Image["Image Layers (read-only)"]
        L1["Layer 1: Base OS\n(ubuntu:24.04)"]
        L2["Layer 2: apt-get install\n(python, pip)"]
        L3["Layer 3: COPY app/\n(application code)"]
        L4["Layer 4: RUN pip install\n(dependencies)"]
    end

    subgraph Container["Container Layer (read-write)"]
        RW["Writable Layer\n(runtime state, logs, temp files)"]
    end

    RW --> L4 --> L3 --> L2 --> L1

    style Container fill:#0db7ed,color:#fff
    style Image fill:#1565c0,color:#fff

Copy-on-Write (CoW)

  • When a container modifies a file from a lower layer, it is copied up to the writable layer
  • Original layers remain unchanged → multiple containers share the same base layers
  • Deleting a file in the container creates a whiteout entry. This hides the lower layer file

BuildKit Pipeline

sequenceDiagram
    participant User as Developer
    participant CLI as docker CLI
    participant BK as BuildKit
    participant Registry as Registry

    User->>CLI: docker build .
    CLI->>BK: Send Dockerfile + context
    BK->>BK: Parse Dockerfile → DAG
    BK->>BK: Resolve cache (local/registry)
    par Parallel Layer Builds
        BK->>BK: Stage 1 (base image)
        BK->>BK: Stage 2 (dependencies)
        BK->>BK: Stage 3 (application)
    end
    BK->>BK: Merge stages → final image
    BK->>Registry: Push (if --push)
    BK->>CLI: Return image ID
    CLI->>User: Successfully built

Key BuildKit Features

Feature Detail
Parallel builds Independent stages execute concurrently
Cache mounts --mount=type=cache for package manager caches
Secret mounts --mount=type=secret — never persisted in layers
Multi-platform Build for arm64, amd64, and others in single command
Registry cache --cache-to type=registry for CI pipelines

Networking Model

flowchart LR
    subgraph Host["Docker Host"]
        subgraph Bridge["docker0 bridge (172.17.0.0/16)"]
            C1["Container 1\n172.17.0.2"]
            C2["Container 2\n172.17.0.3"]
        end
        subgraph UserNet["user-network (10.0.0.0/24)"]
            C3["Container 3\n10.0.0.2"]
            C4["Container 4\n10.0.0.3"]
        end
        IPTABLES["iptables / nftables\n(NAT, port mapping)"]
    end

    Internet["Internet"] <-->|"port mapping\n-p 8080:80"| IPTABLES
    IPTABLES <--> Bridge
    IPTABLES <--> UserNet

    style Bridge fill:#0db7ed,color:#fff
    style UserNet fill:#2e7d32,color:#fff

Network Drivers

Driver Use Case
bridge Default single-host networking. Containers on same host communicate via veth pairs
host Container shares host network namespace. No isolation, maximum performance
overlay Multi-host networking via VXLAN. Used by Swarm/K8s
macvlan Container gets own MAC address. Appears as physical device on network
ipvlan Like macvlan but shares host MAC. L2 or L3 mode
none No networking. Container is completely isolated

Storage Model

Storage Type Lifecycle Use Case
Union FS layers Tied to container Ephemeral container filesystem
Named volumes Independent of container Databases, persistent state
Bind mounts Host path → container Development (live code reload)
tmpfs In-memory only Temporary sensitive data
Volume plugins Driver-dependent NFS, EBS, Ceph, and others

Sources


Benchmarks

Scope

Performance characteristics, scaling limits, and resource consumption for Docker.

Container Performance

Metric Docker Native Overhead
CPU Near-native Baseline < 1%
Memory Near-native Baseline 10-30MB per container
Network (bridge) 90-95% native Baseline 5-10% overhead
Network (host) Near-native Baseline < 1%
Disk I/O (overlay2) 85-95% native Baseline 5-15% overhead
Disk I/O (bind mount) Near-native Baseline < 1%

Image Size Benchmarks

Base Image Size Use Case
scratch 0MB Static Go binaries
alpine 7MB Minimal Linux
distroless 15-25MB Secure, no shell
debian-slim 80MB Compatibility needed
ubuntu 77MB Full Linux tools
node:22-alpine 130MB Node.js apps
python:3.12-slim 150MB Python apps

Build Performance

Strategy Cold Build Cached Build Size Impact
Single stage 30-120s 5-30s Large (500MB+)
Multi-stage 60-180s 10-30s Small (50-150MB)
BuildKit cache 60-180s 3-10s Same
Buildx bake 30-90s 3-10s Parallel builds

Compose Scaling

Containers Memory (runtime) Startup Time Notes
10 100-500MB 5-15s Typical dev setup
50 500MB-2GB 15-45s Medium application
100 1-5GB 30-120s Large stack

Sourcing Status

Unsourced Performance Data

The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.

Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.

Sources


Security

Docker security operates at multiple layers: the host OS kernel (namespaces, cgroups, seccomp, AppArmor/SELinux), the Docker daemon configuration, the container image supply chain, and network isolation. No single layer is sufficient. Defense in depth is required.

See also: infrastructure/docker/index, infrastructure/docker/explanation, infrastructure/docker/how-to-guides

Threat Model

Threat Vector Mitigation
Container breakout to host User namespaces, rootless mode, seccomp, AppArmor
Privileged container abuse Drop capabilities, run as non-root, do not use --privileged
Malicious base images Image signing, vulnerability scanning
Supply chain compromise Content Trust, Cosign, SBOM verification
Daemon socket exposure Restrict /var/run/docker.sock access, use TLS
Network lateral movement Network isolation, user-defined bridges, network policies

Rootless Mode

Rootless mode runs both the Docker daemon and containers without root privileges. Introduced in Docker Engine 20.10, it is the strongest isolation mechanism available.

  • The daemon and containers execute inside a user namespace
  • No SETUID binaries or file capabilities required (except newuidmap/newgidmap for multi-UID support)
  • Container root (UID 0) maps to an unprivileged user on the host
  • Eliminates the risk of container breakout escalating to host root

Rootless vs userns-remap

With userns-remap, the daemon still runs as root but maps container UIDs to unprivileged host UIDs. Rootless mode goes further: the daemon itself has no root privileges.

Limitations of rootless mode: - Some --publish operations require root-owned slirp4netns or rootful kit for privileged ports (< 1024) - Cgroup v2 is required for resource limiting - Some storage drivers and network modes are not available

User Namespaces

User namespaces map container user IDs to different host user IDs:

  • Enabled via --userns-remap in daemon configuration
  • Container root (UID 0) maps to an unprivileged host UID range
  • Each container gets its own UID mapping. This isolates containers from each other
  • Available since Docker 1.10. Can be used alongside rootless mode

Enhanced Container Isolation (ECI)

Docker Desktop Business tier offers ECI, which automatically runs all containers within Linux user namespaces. Each container root maps to a different unprivileged user in the Docker Desktop VM. This provides inter-container isolation without developer workflow changes.

Seccomp Profiles

Seccomp (secure computing mode) restricts the system calls a container can make:

  • Docker uses a default seccomp profile that blocks approximately 44 dangerous syscalls
  • The default profile is applied to all containers unless overridden with --security-opt seccomp=unconfined
  • Custom profiles can be written in JSON to allow or deny specific syscalls with argument filtering
  • Profiles are essential for reducing the kernel attack surface accessible to compromised containers

Common syscalls blocked by the default profile: mount, umount2, swapon, swapoff, pivot_root, reboot, keyctl, init_module, finit_module, delete_module, and more.

AppArmor and SELinux

AppArmor

AppArmor confines containers with per-profile mandatory access controls:

  • Docker generates a default AppArmor profile (docker-default) for each container
  • Profiles restrict file access, capability usage, and network operations
  • Custom profiles can be applied via --security-opt apparmor=PROFILE_NAME
  • Available on Ubuntu, SUSE, and other distributions with AppArmor support

SELinux

SELinux provides mandatory access control on RHEL, CentOS, and Fedora:

  • Docker applies SELinux labels (type: container_t) to container processes
  • File contexts (container_file_t) prevent containers from accessing host files
  • Enables multi-tenancy isolation via MCS (Multi-Category Security) labels
  • Use :z or :Z volume mount flags to relabel volumes for container access

Linux Capabilities

Docker drops most Linux capabilities by default. The default capability set includes:

Capability Purpose
CHOWN Change file ownership
DAC_OVERRIDE Bypass file read/write permission checks
FOWNER Bypass ownership checks on file operations
FSETID Don't clear setuid/setgid on modification
KILL Send signals to processes
NET_BIND_SERVICE Bind to ports below 1024
SETFCAP Set file capabilities
SETGID / SETUID Change process GID/UID

Capabilities can be dropped with --cap-drop or added with --cap-add. Do not use --privileged, which adds all capabilities.

Never use --privileged in production

The --privileged flag grants all capabilities, all device access, and overrides seccomp/AppArmor. It effectively disables container isolation.

Image Signing and Supply Chain Security

Docker Content Trust (DCT)

Docker Content Trust uses Notary (TUF framework) to sign image tags:

  • Enabled by setting DOCKER_CONTENT_TRUST=1 environment variable
  • Signs image metadata (digests, sizes) with repository keys
  • Prevents pulling tampered or unsigned images
  • Key hierarchy: root key, repository key, timestamp key, snapshot key

Cosign (Sigstore)

Cosign is the modern alternative for container image signing:

  • Signs images using key pairs or OIDC-based keyless signing
  • Stores signatures in the same registry as the image (no separate Notary service)
  • Supports transparency log (Rekor) for auditability
  • Integrates with CI/CD pipelines for automated signing
  • Part of the Sigstore project

Vulnerability Scanning

Docker Scout

Docker Scout is Docker's integrated vulnerability analysis tool:

  • Scans images for CVEs using an expanded vulnerability database
  • Compares images against base image updates to show remediation paths
  • Generates SBOM (Software Bill of Materials) attestations
  • Evaluates supply chain attestations (provenance, SBOM)
  • Available in Docker Desktop and Docker Hub

Trivy

Trivy (Aqua Security) is a widely used open-source scanner:

  • Scans OS packages, language dependencies, and IaC files
  • Supports multiple targets: container images, filesystems, git repos, Kubernetes
  • Fast scanning with local vulnerability database caching
  • Integrates with CI/CD (GitHub Actions, GitLab CI)

Grype

Grype (Anchore) is another open-source vulnerability scanner:

  • Focuses on fast, accurate matching against vulnerability databases
  • Supports SBOM input (from Syft) for efficient scanning
  • Configurable matching and severity filtering

Network Isolation

Docker provides several mechanisms for network-level security:

  • User-defined bridges: Provide automatic DNS resolution and isolate containers from the default bridge network. Containers on different user-defined bridges cannot communicate.
  • ICMP/destination port filtering: Docker blocks inter-container ICMP on the default bridge.
  • Publish restrictions: Use --publish to expose only necessary ports.
  • No --network host: Do not use host networking in production. It removes all network isolation.
  • Macvlan caution: Macvlan gives containers direct network presence. This bypasses the Docker firewall. Use only when necessary.

Network hardening

For production workloads, run containers on user-defined bridge networks, never expose the Docker daemon socket, and use external firewalls or cloud security groups to restrict published ports.

Daemon Socket Security

The Docker daemon socket (/var/run/docker.sock) grants full control over the host:

  • Never expose the socket to containers or untrusted users
  • Restrict filesystem permissions on the socket
  • For remote access, configure TLS with mutual authentication (--tlsverify)
  • Use docker context to manage multiple daemon connections securely

Security Checklist

Area Recommendation
Runtime Use rootless mode or userns-remap
Runtime Run containers as non-root (USER directive in Dockerfile)
Runtime Drop unnecessary capabilities (--cap-drop ALL, add only what is needed)
Runtime Apply seccomp profiles (use default at minimum)
Runtime Enable AppArmor or SELinux
Image Use minimal base images (distroless, Alpine, Chainguard)
Image Scan images for CVEs before deployment
Image Sign images with Cosign or Docker Content Trust
Image Pin image digests, not tags (image@sha256:...)
Network Use user-defined bridge networks
Network Never use --privileged or --network host in production
Daemon Restrict access to /var/run/docker.sock
Daemon Enable TLS for remote daemon access
Daemon Keep Docker Engine updated for security patches

References