beginner~4h

Docker Architecture, Engine, Daemon, Client & Registry

Docker is a client-server platform that builds, ships, and runs applications inside isolated Linux (or Windows) processes called containers, using OS-level virtualization instead of full hardware virt

Docker is a client-server platform that builds, ships, and runs applications inside isolated Linux (or Windows) processes called containers, using OS-level virtualization instead of full hardware virtualization.

The platform is made of four cooperating pieces: the Docker Client (CLI/API consumer), the Docker Daemon (dockerd, the engine that does the real work), containerd + runc (the runtime layer that actually creates namespaces/cgroups and starts processes), and a Registry (where images live, such as Docker Hub, GHCR, ECR, or a private registry like Harbor/Nexus).

Conceptually: 'Docker Engine' is the umbrella term for dockerd + containerd + runc + the REST API running on the host. The CLI you type (docker run, docker build) is just a thin client that talks to this engine over a Unix socket (/var/run/docker.sock) or a TCP socket.

  • docker CLI parses your command and sends an HTTP request to the Docker REST API exposed by dockerd, typically over the Unix domain socket /var/run/docker.sock.

  • dockerd handles image management, networking, volumes, and orchestration of build/run requests, but delegates the actual container lifecycle (start/stop/pause) to containerd via gRPC.

  • containerd manages container images, snapshots (layered filesystems), and supervises container processes; it does not implement the low-level OS isolation itself.

  • containerd calls runc (an OCI-compliant runtime) to actually create Linux namespaces (PID, NET, MNT, UTS, IPC, USER) and cgroups (CPU/memory/IO limits), then exec the container's entrypoint process.

  • The container process that results is just a regular Linux process on the host, visible in ps aux on the host, but with a restricted view of the filesystem, network, and process tree.

  • Images are pulled from a Registry using the Docker Registry HTTP API v2 — the daemon resolves the image name to a registry endpoint, downloads layers (gzip tarballs) by content digest, and caches them locally under /var/lib/docker.

  • You type docker run -d -p 8080:8080 myapp:1.0 in the terminal.

  • The Docker CLI serializes this into a JSON payload and sends it to the daemon's REST API.

  • dockerd checks whether myapp:1.0 exists in the local image store (/var/lib/docker/image).

  • If absent, dockerd contacts the configured registry (default: Docker Hub / index.docker.io), authenticates if needed, and pulls each image layer by content digest.

  • dockerd asks containerd to create a container using the image's filesystem snapshot plus a new writable layer on top (via the overlay2 storage driver).

  • containerd delegates to runc, which sets up Linux namespaces (isolated PID/network/mount views) and cgroups (resource limits), then execs the container's ENTRYPOINT/CMD as PID 1 inside the new namespace.

  • dockerd wires up networking (attaches the container to the bridge network, applies the -p port mapping via iptables NAT rules) and returns control to the CLI.

  • The CLI prints the new container ID; the container now runs as a normal Linux process tree on the host, invisible to other containers due to namespace isolation.

  • Local development parity: every engineer runs the exact same Postgres/Redis/Kafka versions via images, eliminating 'works on my machine' bugs.

  • CI pipelines build and test inside ephemeral containers, guaranteeing a clean, reproducible environment for every build.

  • Microservices teams ship one image per service; the registry becomes the single source of truth for 'what is actually deployed' across environments.

  • Solution Architects use the client-server separation to justify remote Docker contexts (docker context) for managing build/test farms or remote Docker hosts securely over SSH/TLS.

  • Never expose the Docker daemon socket (/var/run/docker.sock) over an unauthenticated TCP port — it grants root-equivalent access to the host.

  • Use docker context to manage multiple remote Docker hosts instead of manually exporting DOCKER_HOST.

  • Pin the Docker Engine version in your CI runners and production hosts; engine upgrades can change default behaviors (e.g., BuildKit defaults).

  • Run rootless Docker or restrict daemon access via Unix group membership (docker group) rather than giving broad sudo access.

  • Treating 'Docker' and 'the Docker CLI' as the same thing — confusing client-side flags with daemon-side configuration (e.g., expecting a CLI flag to persist daemon-wide settings).

  • Mounting the Docker socket into a container (-v /var/run/docker.sock:/var/run/docker.sock) without understanding this gives that container full control over the host's Docker daemon — effectively root.

  • Assuming the Docker daemon must be on the same machine as the CLI — remote daemons via TCP/SSH are common in CI/CD and easily misunderstood in interviews.

  • Confusing containerd (used by Docker) with Kubernetes' direct use of containerd as a CRI runtime — they are the same component used in two different orchestration contexts.

  • Use overlay2 storage driver (default and fastest on modern Linux) — avoid legacy drivers like aufs or devicemapper.

  • Keep the local image cache pruned (docker system prune) on CI runners to avoid slow disk I/O from a bloated /var/lib/docker.

  • On Docker Desktop (Mac/Windows), allocate sufficient CPU/RAM to the Docker VM — the daemon runs inside a lightweight VM, not natively, and is a common silent bottleneck.

  • In production, prefer a container runtime managed by an orchestrator (Kubernetes via containerd/CRI-O) rather than raw docker run on a single host — you lose restart policies, scheduling, and self-healing without one.

  • Enable live-restore (live-restore: true in daemon.json) so running containers survive a dockerd restart or upgrade.

  • Centralize daemon configuration via /etc/docker/daemon.json (log driver, storage driver, registry mirrors) and manage it with configuration management (Ansible/Terraform), not manual SSH edits.

  • Run docker info and identify the storage driver and cgroup version in use on your machine.

  • Run curl --unix-socket /var/run/docker.sock http://localhost/v1.46/version and compare the output to docker version.

  • Use docker context create to define a second context pointing at a remote host (or a local TCP port for practice) and switch between contexts.

  • Trace one docker run end-to-end using docker events in one terminal while running a container in another.

  • Docker = CLI (client) + dockerd (daemon) + containerd (container lifecycle) + runc (OCI runtime, namespaces/cgroups).

  • The CLI talks to the daemon over a REST API, typically via a Unix socket.

  • Containers are isolated host processes, not VMs — no hypervisor, shared kernel.

  • Images are pulled from a Registry (Docker Hub, ECR, GHCR, Harbor, etc.) by name and content digest.

  • Securing the daemon socket is one of the most common production security gaps — treat it as root access.

Want a visual for this concept?

Generate a diagram tailored to “Docker Architecture, Engine, Daemon, Client & Registry” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Images, Layers, Caching & Container Lifecycle← Back to all Docker chapters