Learn Docker - Core Concepts & Main Docker Architecture
Episode 2 of 28

Learn Docker - Core Concepts & Main Docker Architecture

Dissecting the Docker client-server architecture: the Docker Client, the daemon (dockerd), and the Registry; the modern runtime stack (containerd, runc) and the role of the OCI standard; and the four core components — Image, Container, Volume, and Network — along with the flow of a command from the CLI down to runc.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Introduction

After understanding in episode 1 why Docker was born — from the bare-metal → VM → container evolution, the "It works on my machine!" problem, to the Linux kernel technologies (cgroups, namespaces, chroot) that became its foundation — in this episode we dissect the machine from the inside: how Docker works, who does what, and how a simple command travels along the entire chain of components.

Understanding the architecture isn't just academic knowledge. When errors appear — for example Cannot connect to the Docker daemon, OCI runtime create failed, or unauthorized: access token has expired — you need to know at which level the problem occurred. Is it the client, the daemon, containerd, or the registry? Being able to map an error to a component is the first diagnostic skill that separates engineers who understand the system from those who only memorize commands.

In this episode we'll dissect the Docker client-server architecture (client, daemon, registry), the modern runtime stack running behind the scenes (containerd, runc, and the OCI standard), the four core Docker components (Image, Container, Volume, Network), and close by tracing the journey of a single command from the CLI until runc creates the container process.

Docker Client-Server Architecture

Docker is built on a client-server pattern — three main actors that are separate from each other:

  1. Docker Client — the docker program you type in the terminal. It only sends commands (via the REST API) and displays responses. It doesn't run or store anything.
  2. Docker Host — the machine (physical or VM) where the daemon (dockerd) runs. This daemon is the operational brain: it receives API requests, manages images, runs and stops containers, and manages networks and volumes.
  3. Docker Registry — image storage. Docker Hub is the default public registry; you can also use private registries like GHCR (GitHub Container Registry) or your company's internal registry.

The most important thing to understand: the client and daemon don't have to be on the same machine. The client talks to the daemon via the API — by default through the Unix socket /var/run/docker.sock, or over the network (for example docker -H tcp://server:2375). This is why you can manage a production server thousands of kilometers away just by typing docker commands on your laptop. This concept is also what lets Docker Context (in episode 24) switch between hosts from a single CLI.

Peta alur: Client → Daemon → Registry
┌─────────────┐   REST API   ┌─────────────┐    pull/push   ┌──────────────┐
│  Client     │ ───────────► │  Daemon     │ ─────────────► │  Registry    │
│  (docker)   │              │  (dockerd)  │                │  Docker Hub  │
└─────────────┘              └─────────────┘                └──────────────┘

Note

When docker pull or docker run seems to "do nothing" — for example the error Cannot connect to the Docker daemon at unix:///var/run/docker.sock — it means the client can't reach the daemon. Check this sequence: is the daemon running? (systemctl status docker) → does the socket exist? → is your docker group correct? We set all of that up in episode 0.

The Modern Runtime Stack: containerd, runc, and OCI

Docker's architecture doesn't stop at dockerd. Since the release of Docker Engine 1.11 (2016), the runtime has been split into separate modular components. Understanding these layers is the key to reading production error logs.

containerd

containerd is the daemon that manages the container lifecycle — the daemon that creates, runs, stops, and deletes containers. It's the de facto industry standard: besides Docker, containerd is also used by Kubernetes (via CRI) and many other runtimes. dockerd no longer manages containers directly — it delegates that work to containerd.

Think of containerd as the project manager: it doesn't build the building itself, but it orchestrates the entire process — when to start, when to stop, who does what.

runc

runc is the low-level runtime that actually creates and runs the container process using kernel primitives: namespaces for isolation, cgroups for resource limits. When a container is "created", it's runc working at the lowest level — it calls the kernel to create a new isolated process.

Continuing the analogy: runc is the contractor who builds the walls and installs the doors — the on-site executor whose hands touch the raw materials (the kernel).

OCI (Open Container Initiative)

OCI is the standards organization (under the Linux Foundation) formed by Docker together with the community to ensure container technology is no longer locked to a single vendor. OCI defines two main specifications:

  • Image-spec — the standard format for container images. As long as an image follows this spec, it can be run by any compatible runtime.
  • Runtime-spec — the contract for runtime behavior: how containers must be created, its config.json configuration file, and how the filesystem and processes are arranged.

This is why the container ecosystem is so rich: a Docker image can be run by Kubernetes, containerd, Podman, or CRI-O — because they all follow the same standard. It's analogous to standard plugs and sockets: as long as it follows the spec, tools from any manufacturer fit.

Hirarki runtime di balik satu kontainer
dockerd ──► containerd ──► runc ──► kernel (cgroups + namespaces)
 (API)       (lifecycle)   (create/run)

The Four Core Docker Components

Beyond process architecture, Docker introduced four abstractions that become your everyday language. Master all four — every episode ahead revolves around these concepts.

Image: The Read-Only Blueprint

Image is a read-only application blueprint that never changes: it contains a base OS (e.g. minimal Ubuntu), runtime (Node.js, Python), libraries, application code, and default configuration. Images are built from stacked layers — something we'll dig deeper into in episode 23. Because it's read-only, an image is safe to share: nobody can accidentally change it.

Think of the image as a cookie cutter: the recipe is fixed, and the result is consistent every time. You can stamp out a thousand identical cookies from a single cutter.

Container: The Running Instance

Container is a running instance of an image. When an image is run, Docker adds a read-write layer on top of the image's read-only layers — this is the "worksheet" where the container's processes write. Changes written here disappear when the container is deleted — an important fact that becomes a major issue in episode 8 (storage).

The best analogy: the image is the cookie cutter, the container is the finished cookie you can eat — and any changes (the topping you added) exist only on that cookie, not in the cutter. Delete the cookie, and the topping disappears with it.

Volume: Persistent Storage

Volume is a data storage mechanism that lives outside the container's lifecycle — the data persists even when the container is deleted and replaced. It's mounted to a specific directory inside the container, but the physical data is stored on the host (or remote storage). A database in production must use volumes, or all data is lost when the container is recreated.

Think of a volume as a filing cabinet that stands outside the room: the room (container) can be dismantled and rebuilt, but the archives stay safe in the cabinet.

Network: The Communication Path

Network governs how containers communicate — between containers, and with the outside world. By default, containers join the bridge network, which gives each container an internal IP so containers can connect to each other while staying isolated from the host. The ports you map (-p 8080:80) are the bridge from the outside world to that internal IP.

Think of networks as small alleys between houses: neighbors (containers) on the same alley can visit each other; outsiders must enter through the main gate (mapped ports).

The Journey of One Command: From CLI to runc

Now let's put it all together. What actually happens when you run the simplest command:

Perintah yang akan kita lacak
docker run hello-world

Notice that this single command passes through six steps across four different components — yet you only see one line of output. This is what good architecture means: complexity is hidden, the interface is simplified. It's still important to know what happens at each step, because in the episodes ahead we'll repeatedly "open the hood" of this chain — when debugging errors, tuning performance, and connecting containers to storage and networks.

The sequence of events behind that one line:

  1. The client (docker) receives the command, parses the arguments, and sends a REST request to the daemon via the /var/run/docker.sock socket. "Run the hello-world image."
  2. The daemon (dockerd) checks whether the hello-world image already exists in the local cache. If not, the daemon downloads it from the registry (Docker Hub) and stores it in the image store.
  3. The daemon asks containerd to create and run a container from that image — along with the configuration (isolation, resource limits, mounts, network).
  4. containerd prepares the "container spec" (following the OCI runtime-spec) and asks runc to create the process.
  5. runc calls the kernel: creates new namespaces, registers a cgroup, then executes the container's main process.
  6. The container process runs — printing the output you see in the terminal — then exits. containerd records its status, and the daemon reports the result back to the client.
Urutan perjalanan satu perintah
docker run hello-world
  │ 1. request API

dockerd ──► cek image lokal ──► (jika belum ada) pull dari registry
  │ 2. minta create + start

containerd ──► siapkan spesifikasi OCI
  │ 3. minta runc

runc ──► kernel: namespaces + cgroups ──► proses kontainer berjalan

You'll never see most of this chain directly — but when errors appear, this map becomes your diagnostic tool. Cannot connect = a problem at step 1. pull access denied = a problem at step 2 (registry). OCI runtime create failed = a problem at steps 4–5 (runc/kernel). The docker inspect error details in episode 4 will reveal the details of each of these levels.

Important

Don't confuse "Docker Engine" with the surrounding ecosystem. Docker Engine (what you installed in episode 0) includes the client, dockerd, and the containerd/runc integration. Docker Desktop is an application that wraps the Engine for macOS/Windows with an added VM, GUI, and features. Containerd can stand alone with no Docker at all — that's what Kubernetes uses. They all share the same OCI foundation.

Reading Errors by Architecture Layer

The biggest practical benefit of understanding the architecture is the ability to pinpoint the location of an error within seconds. Every Docker error message indicates which layer failed — and each layer has its own troubleshooting recipe:

Common ErrorFailed LayerFirst Action
Cannot connect to the Docker daemon at unix:///var/run/docker.sockClient → daemon (step 1)Check the daemon is running (systemctl status docker), the socket exists, the docker group is correct
pull access denied for <image>, repository does not exist or may require 'docker login'Daemon → registry (step 2)Check the image name, or docker login for private images
unauthorized: authentication requiredRegistry (token/credential)docker login, check the token hasn't expired
OCI runtime create failed: ... container_linux.go ...containerd/runc (steps 4–5)Check the storage driver, disk space, and host kernel
port is already allocatedNetwork / daemonUse ss -tlnp to find the port's user, then change the host port
Container Exited repeatedly for no reasonApplication processdocker logs + docker inspect State.ExitCode

Tip

A healthy habit: read the error from the innermost clause — the part that mentions the most specific file, line, or system name usually reveals the root cause, not the outer part that only wraps it. OCI runtime create failed sounds scary, but the clause after the colon (e.g. mount ...: no space left on device) is what actually answers: the disk is full.

Conclusion

In episode 2 you've learned that Docker is a client-server architecture with three actors: the client (docker) that sends commands via the REST API, the daemon (dockerd) that acts as the operational brain, and the registry (Docker Hub and others) as the store of portable images. Behind the scenes, dockerd delegates the container lifecycle to containerd (the industry-standard lifecycle manager), which in turn asks runc (the low-level runtime) to create the container process using namespaces and cgroups — all standardized by OCI through the image-spec and runtime-spec.

The core takeaways:

  • The client sends commands, the daemon does the work, the registry stores images — all three can be on different machines.
  • dockerdcontainerdrunc: from the API, to lifecycle management, to kernel process execution.
  • Image is a read-only blueprint; Container is a running instance with a read-write layer; Volume is persistent data outside the lifecycle; Network is the communication path.
  • OCI makes the container ecosystem interoperable — a Docker image runs anywhere.
  • Errors have a "location": this architecture map is your first diagnostic tool.

Now you know how Docker works from the inside. In the next episode, episode 3, we get hands-on: running your first containerdocker run hello-world, docker run -it ubuntu bash, detached mode, container management (ps, stop, start, rm), docker exec and docker logs, and port mapping that connects containers to the outside world. Time to get our hands dirty! See you in episode 3.