Containers Explained: Docker, Images, and Deployment

Containers are not lightweight virtual machines. They are regular Linux host processes isolated by kernel namespaces and constrained by control groups. Here is how modern containerization actually functions under the hood.

Industrial shipping cargo containers representing modular Docker architecture
Figure 1: Standardization of transport: the maritime analogy behind standardized OCI container images.

1. Solving "It Works on My Machine"

Historically, deploying software required ensuring the target production server had identical system libraries, runtime versions, environment variables, and configuration paths as the developer’s workstation. A minor discrepancy in an installed C shared library (e.g. glibc 2.31 vs 2.35) caused catastrophic runtime segfaults.

Containers bundle the application executable alongside its exact filesystem dependencies—every library, binary, and config file—into a single immutable, portable artifact that runs identically anywhere a Linux kernel exists.

Server chassis running container daemon runtimes
Figure 2: Bare metal host servers running thousands of isolated container worker processes.

2. Containers vs. Virtual Machines

A Virtual Machine runs a complete guest operating system—including its own dedicated kernel, memory manager, device drivers, and system services—on top of a hypervisor. This introduces heavy CPU/RAM overhead and multi-minute boot times.

A Container is simply a regular Linux host process that shares the host operating system’s kernel. Because there is no guest kernel to boot, containers instantiate in milliseconds and consume virtually zero idle memory overhead.

3. Linux Kernel Primitives: Namespaces and cgroups

Docker did not invent container technology; it built an ergonomic user experience around two foundational Linux kernel features:

  • Namespaces (Isolation): Determines what a process can see. Namespaces isolate process IDs (PID), network interfaces and routing tables (NET), filesystem mount points (MNT), inter-process communication (IPC), hostnames (UTS), and user mappings (USER).
  • Control Groups (cgroups): Determines what a process can use. Enforces strict hardware resource quotas on CPU shares, physical RAM limits, disk I/O bandwidth, and network priority.
Terminal shell prompt inspecting running Docker containers
Figure 3: Inspecting container layers, image digests, and process metrics via docker CLI.

4. OverlayFS & Copy-on-Write (CoW) Layers

Container images are composed of stacked read-only filesystem layers managed by OverlayFS.

When a container launches, Docker places a thin, mutable Read-Write container layer on top of the immutable image layers. If the container modifies an existing configuration file, OverlayFS copies the file up to the read-write layer (Copy-on-Write) before editing it. When the container is destroyed, that thin read-write layer is deleted, leaving the underlying base image pristine.

5. Writing Production Dockerfiles

A Dockerfile contains procedural instructions for compiling an image:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
Silicon printed circuit architecture representing optimized build layers
Figure 4: Algorithmic caching: preserving immutable build layers across deployment cycles.

6. Multi-Stage Build Optimization

To keep production images tiny and secure, use multi-stage builds. Compile your code inside a heavy build stage equipped with compilers and SDKs, and copy only the compiled binary into a lightweight minimal production image:

# Stage 1: Build binary in Go
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /bin/app

# Stage 2: Minimal final runtime image (under 15MB!)
FROM alpine:3.19
COPY --from=builder /bin/app /bin/app
USER nobody
ENTRYPOINT ["/bin/app"]

7. Volumes and Bridge Networking

Containers are ephemeral by default—when a container stops, data written to its local filesystem vanishes. To persist database records, attach a Docker Volume:

$ docker run -d -v postgres_data:/var/lib/postgresql/data postgres:16

8. Multi-Container Orchestration with Docker Compose

Modern applications involve web servers, background workers, and databases working together. docker-compose.yml coordinates these dependencies declaratively:

services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://root:secret@db:5432/app
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

9. Frequently Asked Questions

Why shouldn't I run containers as the `root` user?

If an application inside a container is compromised by a remote code execution exploit, running as root inside the container makes container-escape vulnerabilities dramatically easier, potentially granting the attacker root control of the host machine.

What is Kubernetes (k8s)?

Docker manages containers on a single host. Kubernetes is an enterprise orchestration platform that automates container deployment, autoscaling, load balancing, and self-healing across hundreds of physical servers in a cluster.