Skip to content
Back to all concepts
DevOpsContainersLinuxLow Level

Docker Internals

What actually is a container? Just a Linux process with a mask on. Deep dive into Namespaces, Cgroups, and Union Filesystems (OverlayFS).

By ScaleWikiUpdated 3 min readAdvanced

Containers vs Virtual Machines

Virtual Machine (VM): Emulates hardware. Runs a full Guest OS on top of a Hypervisor. Heavy (GBs). Container: Shares a kernel with its host environment. Image size and startup time depend on the application; Linux containers on a non-Linux desktop normally run inside a Linux VM.

Containers are process isolation mechanisms provided by the Linux Kernel.

The Three Pillars of Containers

How does docker run actually work? It uses three Linux features:

1. Namespaces (Isolation)

Namespaces limit what a process can see.

NamespaceWhat it Isolates
PIDProcess identifiers. The first process in a new PID namespace has PID 1 there.
NETNetwork Interfaces (eth0), Ports, Routing Tables.
MNTMount Points. Container has its own root /.
UTSHostname and Domain.
IPCInter-Process Communication (Shared Memory).
USERUser/group ID mappings, when a user namespace is configured. Container root is not automatically remapped by every configuration.

Try it: unshare --fork --pid --mount-proc /bin/bash creates a new PID namespace.

2. Control Groups (Cgroups) (Resource Limiting)

Cgroups limit how much a process can use.

  • Memory: "You get 512MB RAM." (OOM Kill if exceeded)
  • CPU: "You get 0.5 CPU cores."
  • Block I/O: "Limit disk read speed to 10MB/s."

Accessed via file system: /sys/fs/cgroup/.

3. Union Filesystem (Storage)

How are images so efficient?

Docker Images are built from Layers.

  • Layers: Read-only changesets (e.g., Ubuntu Base -> Add Python -> Add App Code).
  • Union Mount: Merges all layers into a single view.
  • Copy-on-Write (COW): If a container modifies a file from a lower layer, it copies it up to the writable top layer first.

OverlayFS is a common Linux implementation; the configured storage driver or snapshotter determines the actual behavior.

Docker Architecture

Client-Server model.

  1. Docker CLI: Sends commands (docker run) to Daemon via REST API (Unix Socket).
  2. Docker Daemon (dockerd): Manages objects (images, containers, networks).
  3. Containerd: High-level runtime. Manages image pull/push and lifecycle.
  4. Runc: Low-level runtime. Spawns the actual process using syscalls.

Building Efficient Images

1. Multi-Stage Builds

Build in a fat image (Go compiler), copy binary to tiny image (Alpine/Scratch).

dockerfile
# Stage 1: Build
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /myapp main.go

# Stage 2: Run
FROM scratch
COPY --from=builder /myapp /myapp
USER 65532:65532
ENTRYPOINT ["/myapp"]

The sample assumes a pure-Go program that needs no external files, certificates or dynamic libraries. A real build should pin maintained base images by version/digest and include any runtime assets. Smaller final images are the purpose of multi-stage builds, not a guaranteed size reduction.

2. Layer Caching

Order matters! Put frequently changing instructions (COPY code) at the bottom.

dockerfile
# BAD: Breaks cache if source code changes
COPY . .
RUN npm install

# GOOD: Caches dependencies unless package.json changes
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

Security: Container Breakout

If a container runs as root, and a vulnerability exists in the Kernel... Container Breakout: Attacker escapes the container and gains access to the Host.

Defenses:

  • Run as Non-Root: USER appuser in Dockerfile.
  • Read-Only Root: docker run --read-only.
  • Capabilities: Drop unused Linux privileges (--cap-drop ALL --cap-add NET_BIND_SERVICE).

Worked decision: an image that builds but will not start

A binary copied from a glibc-based builder into Alpine can fail because the expected dynamic loader is absent. Before blaming container networking, inspect the binary’s dependencies and target architecture. For a pure-Go example, disabling CGO makes a scratch image possible; an application that uses a native database driver needs a compatible runtime instead. Test the final image as its non-root user, including certificate loading and writable-directory requirements.