The Engine: Client, Daemon and Runtime

Follow docker run all the way down through the REST API, the daemon, containerd and runc — and understand why the daemon can restart without killing your containers.

beginner 18 min lesson hands-on task included

Most people learn docker run as a magic word. It is worth about twenty minutes to learn what it actually calls, because almost every confusing Docker error comes from one specific layer of the stack, and knowing the layers tells you which log to read.


Topic 1: Client–Server, Not a Single Program

Docker uses a client-server architecture. The docker binary you type is a client. It does almost nothing itself.

ComponentRole
Docker CLIParses your command, turns it into an HTTP request
REST APIThe interface the daemon exposes; everything the CLI can do, a program can do
Docker daemon (dockerd)Long-running process that builds images, manages containers, networks and volumes

On Linux the CLI talks to the daemon over a Unix socket at /var/run/docker.sock. That single fact explains two things you will meet later:

docker version          # prints Client and Server blocks separately — they are separate programs
docker info             # daemon-side view: storage driver, cgroup driver, container counts

If docker version shows a client but errors on the server block, the daemon is not running or you cannot reach its socket. That is a completely different problem from an image or container fault, and it is the first thing to check.

Why you are told to add your user to the docker group:

sudo usermod -aG docker "$USER"   # log out and back in for it to take effect
sudo systemctl enable --now docker

Membership of the docker group grants write access to the daemon socket. Be clear-eyed about what that means: anyone who can talk to that socket can start a container that mounts the host’s root filesystem, which is effectively root on the host. Adding a user to the docker group is granting root. It is fine on your laptop; on a shared build server it is a security decision, not a convenience.


Topic 2: What docker run hello-world Actually Does

Walk the whole path once:

  1. The CLI serialises your command into a REST call and sends it to dockerd.
  2. The daemon looks for the image hello-world in its local image store.
  3. Not there? The daemon contacts the configured registry — Docker Hub by default — and pulls the image, layer by layer.
  4. The daemon asks containerd to create a container from that image.
  5. containerd unpacks the image layers into a filesystem bundle and calls runc.
  6. runc forks, sets up namespaces and cgroups for the new process, and execs the container’s entrypoint. Then runc exits — it is not a supervisor.
  7. Output is streamed back up: container → containerd → daemon → your terminal.

Run the same command a second time and step 3 disappears. The image is already local; the daemon goes straight to creation. That is why the second run feels instant.

docker CLI ──REST──▶ dockerd ──gRPC──▶ containerd ──▶ runc ──▶ your process
                                                       (exits after setup)

Topic 3: Why the Runtime Is Split Up

Early Docker created containers by shelling out to LXC. Linux releases kept changing LXC’s behaviour underneath Docker, so Docker wrote its own container-creation library — runc — and later donated the runtime layer to the community. The result is the layering above, and it buys two concrete things:

Standardisation. runc implements the OCI runtime specification and images follow the OCI image specification. That is why Podman, containerd, CRI-O and Kubernetes can all consume the images you build with Docker. You are not producing a Docker-proprietary artefact; you are producing an OCI image.

Daemon-independent container lifetime. Because runc exits after setup and containerd supervises containers separately from dockerd, you can upgrade or restart the Docker daemon and running containers keep running. Try it — it is the hands-on task for this lesson. This is not a trivia point; it is what makes patching the Docker engine on a production host something other than an outage.


Topic 4: Images and Containers Are Different Things

The single most common conceptual mix-up, so state it precisely:

  • An image is a read-only packaging format: your application code or binaries, the runtime, the dependencies, and the filesystem around them. It is inert. It cannot be edited — only deleted or replaced by building a new one.
  • A container is a running (or stopped) instance of an image, with a thin writable layer on top.

One image, many containers. Start ten containers from nginx:1.27 and you have one copy of the image on disk plus ten small writable layers. This is the density story from lesson 1 made concrete.

docker pull nginx                    # gets nginx:latest — pin the tag instead
docker pull nginx:1.27-alpine        # name:tag, the naming convention for every image
docker images                        # local image store: repository, tag, image ID, size
docker rmi nginx:1.27-alpine         # delete by name:tag, or by image ID

docker rmi nginx with no tag deletes nginx:latest specifically, not everything called nginx. That surprises people at least once.


Topic 5: The Container Lifecycle and Its States

A container is not simply on or off. There are six states, and docker ps -a will show you every one of them:

StateMeaning
createdCreated (e.g. by docker create) but never started
runningCurrently executing its main process
pausedProcesses frozen with the freezer cgroup — still resident, not scheduled
restartingIn the middle of a restart, driven by a restart policy
exitedRan and stopped; the writable layer still exists
deadThe daemon tried to stop it and failed, usually a busy device or mount

The lifecycle verbs map onto those states directly:

docker create alpine sleep 1d        # → created
docker start <id>                    # → running
docker pause <id>                    # → paused
docker unpause <id>                  # → running
docker stop <id>                     # SIGTERM, wait, then SIGKILL → exited
docker kill <id>                     # SIGKILL immediately → exited
docker restart <id>                  # stop then start
docker rm <id>                       # destroy, releasing the writable layer

Three details that come up constantly:

docker run is create + start. Nothing more mysterious than that.

You cannot docker rm a paused container. It must be stopped first. docker rm -f will force it by killing the container, which is exactly what you are trying to avoid on anything that matters.

Prefer stop then rm over rm -f. docker stop sends SIGTERM and gives the process its grace period (10 seconds by default) to flush buffers, close connections and exit cleanly. rm -f does not extend that courtesy in the same way. On a database or a queue consumer, the difference is between a clean shutdown and a recovery on next start.

docker stop api && docker rm api          # the polite version
docker rm -f $(docker ps -aq)             # the "clear my laptop" version

Topic 6: Reading the State of a Host

Four commands, and you can describe any Docker host you are dropped onto:

docker ps                    # running containers
docker ps -a                 # every container, including exited ones
docker info                  # daemon-wide: driver, cgroup version, counts, warnings
docker stats                 # live CPU, memory, network and block I/O per container
docker events                # a live stream of everything the daemon is doing

docker info deserves a slow read the first time. It tells you the storage driver (should be overlay2 on any modern Linux — lesson 5), the cgroup version (v2 on current distributions, which changes how memory limits are reported), and the counts of running, paused and stopped containers. Warnings at the bottom of that output are real warnings; do not scroll past them.

docker stats and docker events are the two monitoring primitives the engine gives you: stats for resource usage, events for daemon activity. Neither is a monitoring system, but at 3am on a single host, both are faster than one.

Try it yourself: Run docker events in one terminal, then in another run docker run --rm alpine echo hi. Watch the create, attach, start, die and destroy events go past in order. That stream is the daemon narrating the lifecycle from Topic 5.

Common mistake: Assuming docker ps showing nothing means the host is clean. docker ps hides exited containers, and every exited container is still holding its writable layer on disk. docker ps -a is the honest view — and a host that has been running CI for a year without docker system prune is usually where the missing disk space went.