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 auxon 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.0in 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.0exists 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 contextto 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 runon a single host — you lose restart policies, scheduling, and self-healing without one. -
Enable live-restore (
live-restore: truein 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 infoand identify the storage driver and cgroup version in use on your machine. -
Run
curl --unix-socket /var/run/docker.sock http://localhost/v1.46/versionand compare the output todocker version. -
Use
docker context createto define a second context pointing at a remote host (or a local TCP port for practice) and switch between contexts. -
Trace one
docker runend-to-end usingdocker eventsin 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 →