🏗️ Docker Architecture
Last lesson you learned what a container is. Now meet the machinery that actually builds and runs one. When you type docker run, that command is not the whole story — it's a message handed off to a background service that does the real work. Understanding this handoff is what turns Docker from a set of memorized incantations into a system you can reason about and debug.
Week 11 · Day 1 (Monday: Docker Fundamentals) · Lecture 2
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Describe Docker's client–server architecture and name its major parts
- Explain the roles of the Docker client, the daemon (dockerd), and the registry
- Trace step by step what happens when you run
docker run nginx - Distinguish the four core Docker objects: images, containers, networks, and volumes
- Explain how the daemon delegates real execution to
containerdandrunc - Use inspection commands to observe the architecture on your own machine
Estimated Time: 60 minutes
Practice: Run docker version and docker info and identify each architectural component in the output.
In This Lesson
The Big Picture
Understanding Docker's architecture is like knowing how a car works under the hood. You can drive without it — but the moment something goes wrong, or you want to tune performance, that knowledge is the difference between guessing and diagnosing.
At the highest level, Docker has three players: a client you type commands into, a daemon that does the actual work, and a registry that stores images. Everything else in this lesson is detail hanging off this triangle.
the docker command"] -->|REST API| B["Docker Daemon
dockerd"] B -->|manages| C["Images & Containers"] B -->|pulls / pushes| D["Registry
Docker Hub"]
Keep that picture in mind. Next we zoom into each corner.
Client–Server Architecture
Docker uses a client–server model. The part you interact with (the client) is deliberately separate from the part that does the heavy lifting (the server, i.e. the daemon). They talk over a well-defined REST API — usually over a local socket, but it can be over the network too.
📖 The restaurant analogy
You (the user) tell the waiter (Docker client) what you want. The waiter writes it on a standard order slip (the REST API) and passes it to the kitchen (the daemon), which actually cooks the meal (builds images, runs containers). The waiter then brings the result back to you. You never step into the kitchen yourself — and you don't need to, as long as the order slip is understood by both sides.
This separation is powerful: because the client and daemon communicate over an API, the client on your laptop can control a daemon running on a remote server just as easily as a local one. It's also why third-party tools (Docker Desktop's GUI, Portainer, VS Code's Docker extension) can all drive the same daemon — they speak the same API.
The Docker Client
The Docker client is the docker command-line tool — the primary way you interact with Docker. Its job is narrow and clear:
- Accept commands you type (
docker build,docker run,docker pull, …) - Translate them into REST API calls to the daemon
- Display the daemon's results and output back to you
Notice what the client does not do: it never runs a container itself. It's a messenger. When you run this:
# You type this in your terminal (the client):
docker run -d -p 8080:80 nginx
…the client packages "run a detached nginx container publishing port 8080 to 80" into an API request and sends it to the daemon. The daemon does everything else.
Because the client only talks to the daemon over an API, you can point it at a different daemon entirely:
# Point the client at a remote daemon over TCP
export DOCKER_HOST=tcp://remote-docker-host:2375
docker info # now reports the REMOTE daemon's info
⚠️ A remote daemon is a security surface
Exposing the daemon over plain TCP (port 2375) gives anyone who can reach it full control of the host — access to the Docker socket is effectively root access. In real setups you'd protect it with TLS or, better, use SSH (DOCKER_HOST=ssh://user@host). Recognize the pattern, but don't leave an unprotected TCP daemon exposed.
Think of the client like a TV remote: the same remote can control different TVs (daemons) depending on which one it's pointed at.
The Docker Daemon (dockerd)
The Docker daemon, the program named dockerd, is the persistent background service that does all the real work. It listens for API requests and manages every Docker object on the host.
If the client is the waiter, the daemon is the entire kitchen: it's where fuel becomes motion. You rarely interact with it directly, but nothing happens without it. (If you ever get "Cannot connect to the Docker daemon," it means the kitchen is closed — the daemon isn't running.)
The daemon delegates the real execution
Early Docker was monolithic — the daemon did everything itself. Modern Docker is layered and standards-based. The daemon doesn't personally spawn container processes; it hands that job down a chain of specialized, OCI-compliant components:
high-level daemon"] --> B["containerd
manages lifecycle"] B --> C["runc
spawns the process"] C --> D["Running Container"]
- containerd — a core runtime that manages the full container lifecycle (pulling images, starting/stopping) and can be reused by other tools, including Kubernetes.
- runc — a small, portable tool that implements the OCI runtime spec and actually creates the container process by talking to the kernel's namespaces and cgroups.
Kitchen analogy again: the head chef (dockerd) coordinates, the sous-chef (containerd) manages the flow of dishes, and the line cook (runc) does the hands-on cooking of each individual dish. The split means each piece can be improved independently — and lower layers like containerd can power other systems entirely.
Registries
A registry is a store for Docker images — the place the daemon pulls images from and pushes images to. It's the "GitHub for images."
Docker Hub"] B -->|docker pull| C["Server / Teammate"]
- Docker Hub — Docker's public registry and the default. When you
docker pull nginx, it comes from here. - Private registries — self-hosted or cloud (Amazon ECR, Google Artifact Registry, Azure Container Registry, GitHub Container Registry) for your organization's own images.
# Pull a public image from Docker Hub (the default registry)
docker pull nginx:latest
# Tag your local image for your namespace, then publish it
docker tag myapp:1.0 yourusername/myapp:1.0
docker push yourusername/myapp:1.0
# Pull from a private registry by fully-qualified name
docker pull registry.example.com/myapp:1.0
A registry is like a library of blueprints: anyone with access can check out a copy to build their own structure, and the standard format means the same blueprint works everywhere.
💡 Registries add security features
Serious registries offer authentication (who can push/pull), image signing (verify an image wasn't tampered with), and vulnerability scanning (flag known CVEs in your image layers). You'll lean on these when you deploy for real.
What Happens on docker run
Let's tie the pieces together by tracing one command end to end. You type docker run nginx on a fresh machine that has never seen the nginx image. Here's the conversation between the parts:
- The client turns your command into a REST API request and sends it to the daemon.
- The daemon checks whether the
nginximage is already in its local store. - It isn't, so the daemon pulls it from the registry (Docker Hub by default), downloading the image layers.
- The daemon creates a container from the image and starts it, delegating the actual process creation to
containerdandrunc. - The daemon reports status back to the client, which prints it for you.
Run the same command a second time and step 3 disappears — the image is already local, so the daemon goes straight to creating the container. That's the layer cache from the previous lesson working at the whole-image level.
Docker Objects
The daemon manages four core kinds of object. You'll spend the rest of the week working with all of them.
Images
A read-only template built from a Dockerfile, composed of layers, identified by name and tag (e.g. node:20-alpine). Images are the blueprint — you don't live in a blueprint, you live in the house built from it.
Containers
A runnable instance of an image, with its own filesystem, network stack, and process space. Containers can be started, stopped, moved, and deleted, and connected to networks and volumes. If the image is the blueprint, the container is the actual house with people living in it.
Networks
How containers talk to each other and the outside world. The default bridge driver puts containers on a private virtual network; host removes isolation and uses the host's network directly; none disables networking; overlay spans multiple hosts (used with orchestration).
Volumes
Docker-managed persistent storage that outlives any single container. Because containers are ephemeral, anything that must survive a restart or redeploy — a database's data, user uploads — belongs in a volume.
A volume is like a storage unit you can attach to any house: move out of one container and into another, and your stored belongings come with you, intact.
| Object | What it is | Lives as long as… |
|---|---|---|
| Image | Read-only blueprint of layers | You keep it (until docker rmi) |
| Container | Running instance of an image | Its lifecycle (until removed) |
| Network | Virtual network connecting containers | You keep it (until docker network rm) |
| Volume | Persistent, container-independent storage | You keep it (survives container removal) |
Practice & Quiz
🏋️ Exercise 1: Read the architecture in docker version
Goal: See the client–server split with your own eyes. Run the command below and identify which block describes the client and which describes the server (the daemon).
docker version
💡 Hint
The output is divided into two labelled sections. One is literally headed "Client" and the other "Server." The Server section is your daemon; look for a line mentioning containerd and runc underneath it.
✅ Solution
The Client block shows your docker CLI version and API version. The Server block shows the daemon (Engine), plus the versions of containerd and runc it delegates to. Seeing both in one command is the client–server architecture: the client asked the server for its version and printed both. If the Server block is missing, the daemon isn't running.
🏋️ Exercise 2: Trace the pull
Goal: Predict, then observe. On a machine that has never pulled hello-world, run it and watch the output. Which architectural components are involved, and in what order?
docker run hello-world
✅ Solution
The client sends the run request to the daemon; the daemon finds no local hello-world image, so it prints "Unable to find image… locally" and pulls it from Docker Hub (the registry); then it creates and starts a container via containerd/runc; the container prints its message and exits; the client relays that output to your terminal. Run it a second time and the pull step is skipped — the image is now local.
🎯 Quick Quiz
Question 1: When you type docker run, which component actually creates and runs the container?
Question 2: How does the Docker client communicate with the daemon?
Question 3: Which Docker object should hold a database's data so it survives the container being removed?
Best Practices & Pitfalls
✅ Do
- Reach for
docker infoanddocker versionfirst when troubleshooting — they reveal daemon state - Protect a remote daemon with TLS or SSH; treat socket access as root access
- Use named volumes for anything that must persist
- Let the daemon and its runtimes stay current —
containerd/runcupdates carry security fixes
❌ Don't
- Expose the daemon over plain, unauthenticated TCP
- Assume the client "runs" containers — it only sends requests
- Store important data in a container's writable layer
- Ignore "Cannot connect to the Docker daemon" — it means the daemon isn't running, not that your command is wrong
⚠️ The Docker socket is root
Anyone who can reach /var/run/docker.sock can start a container that mounts the host filesystem and effectively becomes root on the host. Be extremely careful about who — and which containers — you grant socket access to.
Summary
🎉 Key Takeaways
- Docker is client–server: the client sends commands, the daemon does the work, and they talk over a REST API
- The daemon (dockerd) manages every Docker object and delegates real execution down to
containerdthenrunc - A registry (Docker Hub by default) is where images are pulled from and pushed to
- On
docker run: client → daemon → (pull if needed) → create & start container → results back to you - The four core objects are images, containers, networks, and volumes — volumes are what make data persist
📚 Additional Resources
- Docker Docs — Docker architecture
- Docker Docs — Engine API reference
- Docker Docs — Networking overview
- Docker Docs — Volumes
🚀 What's Next?
Now that you know how the parts fit together, it's time to drive. Next up: Basic Docker Commands — the day-to-day vocabulary (run, ps, images, exec, logs, stop, rm) that lets you create, inspect, and clean up containers with confidence.
🎉 You've seen under the hood!
Every command in the next lesson is just a message to the daemon — you now know exactly where it goes and what it triggers.