21.2. Docker Architecture
Table of Contents
Big Picture of Docker Architecture
Docker is built with a client server architecture. Understanding how its pieces fit together will make everything else about containers much clearer.
There are three core parts:
- Docker client
- Docker daemon (server)
- Registries (like Docker Hub)
Around these, there are additional pieces such as images, containers, networks, and volumes that you will use every day.
In this chapter we stay at the architecture level so you see how these parts talk to each other and where they run.
Docker Client
The Docker client is what you interact with directly. It is usually the docker command in your terminal.
Example commands:
docker ps
docker run nginx:latest
docker build -t myapp:1.0 .Key ideas:
- The client is a front end.
It does not run containers itself. It sends instructions to the Docker daemon. - The client can talk to:
- A local daemon on your machine.
- A remote daemon on another server.
- Multiple daemons, depending on configuration and context.
- Communication is done over:
- A Unix socket on Linux/macOS, for example
/var/run/docker.sock. - A named pipe on Windows, for example
//./pipe/docker_engine. - Or a TCP socket, for remote daemons, for example
tcp://192.168.1.10:2376.
You can think of the client as a REST API client for the Docker Engine API. It sends HTTP requests, receives HTTP responses, and prints human friendly output.
Example of pointing the client to a remote daemon:
export DOCKER_HOST=tcp://my-server:2376
docker ps
The docker command is just one client. Docker also provides:
docker composefor multi container applications.- SDKs for Go, Python, etc., that can also act as clients to the daemon.
Docker Daemon (Server)
The Docker daemon is the long running background process that actually manages:
- Images
- Containers
- Networks
- Volumes
On most systems the main daemon process is dockerd.
Responsibilities:
- Accept API requests from clients.
- Pull images from registries.
- Build images.
- Create, start, stop, and delete containers.
- Manage networking interfaces for containers.
- Manage storage, volumes, and image layers.
A simple lifecycle when you run:
docker run nginx:latest- Client sends a request to the daemon:
"Create a container from imagenginx:latestand start it." - Daemon checks if
nginx:latestis available locally. - If not, it pulls the image from a registry.
- Daemon creates a container:
- Sets up a filesystem from the image layers.
- Configures network interfaces, ports, and environment variables.
- Sets resource limits if requested.
- Daemon starts the container process.
- Daemon returns information to the client, which prints output.
The daemon can run:
- On your development laptop.
- On a remote Linux server.
- Inside a virtual machine in the cloud.
Multiple clients can talk to the same daemon at the same time.
The daemon has root level access to the host by default.
If you expose the Docker daemon API over TCP without proper authentication and TLS, anyone who can connect to it can fully control the host.
Never expose the daemon publicly without strong protection.
Docker Engine and Components
The full system that includes the daemon and its low level container runtime is often called Docker Engine.
From top to bottom there are several layers.
High level architecture:
- Docker Client
- Docker Engine API
- Docker Daemon (
dockerd) - Container runtime (for example
containerd,runc) - Linux kernel features (namespaces, cgroups, union file systems)
You usually do not work with the lower layers directly, but knowing they exist helps when debugging.
Docker Engine API
The Engine exposes a REST API. Every docker command turns into HTTP calls, for example:
GET /containers/jsonfordocker psPOST /containers/createfordocker createPOST /images/createfordocker pull
You can call this API directly from your own tools or backend services if you want to automate container operations.
Example, using curl to list containers on a Linux host:
curl --unix-socket /var/run/docker.sock http://localhost/containers/jsonContainer Runtime
The daemon does not talk directly to the kernel. Instead it uses lower level runtimes.
Typical stack:
dockerdtalks tocontainerd.containerdusesruncto create and run containers according to the OCI standard.
You rarely interact with these components directly in everyday backend work, but they explain how Docker can plug into other systems like Kubernetes.
Images and Layers
An image is an immutable blueprint from which containers are created.
Architecturally, images are made of layers. Each layer represents a filesystem change.
Example Dockerfile:
FROM python:3.12-slim # Layer 1: base system + Python
WORKDIR /app # Layer 2: metadata change
COPY requirements.txt . # Layer 3: adds file
RUN pip install -r requirements.txt # Layer 4: installs packages
COPY . . # Layer 5: application code
CMD ["python", "main.py"] # Metadata: default commandWhen you run:
docker build -t myapp:1.0 .The daemon:
- Reads the Dockerfile.
- Builds each instruction into a new layer.
- Stores layers in its local image store.
- Tags the final image as
myapp:1.0.
Layers are stored in a union filesystem so they appear as a single filesystem to the container, but underneath they are stacked.
Important consequences:
- Layer reuse
If two images share the same base image, that base layer is downloaded and stored only once. - Faster builds
If only the last layer changes, Docker reuses earlier layers from cache.
Example:
- Image A:
python:3.12-slim+ dependencies + app v1 - Image B:
python:3.12-slim+ same dependencies + app v2
The large base and dependency layers are shared. Only the final app code layer differs.
Important rule: Images are immutable.
After an image is built, you do not change it. You build a new image instead, usually with a new tag such as myapp:1.1.
This immutability is a core part of reliable deployments.
Containers as Running Instances
A container is a running instance of an image plus some runtime configuration.
Relationship:
- One image can have many containers.
- Containers are created, started, stopped, and deleted.
- The image remains unchanged.
When the daemon creates a container:
- It takes the image layers.
- Adds a thin writable layer on top.
- Starts the process defined by
CMDorENTRYPOINT. - Applies isolation:
- Namespaces for process IDs, network, mounts, etc.
- Control groups (cgroups) for CPU and memory limits.
From inside the container, the process sees:
- Its own filesystem, from the image.
- Its own process tree.
- Its own network interfaces and hostname.
From outside, you can inspect containers:
docker ps # list running containers
docker inspect ID # show full configuration and stateThink of a container as:
- Like a very lightweight virtual machine, but using the host kernel.
- Just a normal process on the host, with strong isolation.
Registries and Repositories
A registry is a server that stores and distributes images.
A repository is a collection of related image tags under a name in a registry, for example library/nginx.
Public registry example:
- Docker Hub at
hub.docker.com.
Private registry examples:
- GitHub Container Registry (
ghcr.io). - GitLab Container Registry.
- Self hosted Docker Registry.
Flow when you use docker pull:
- Client:
docker pull nginx:latest. - Daemon: Contacts the registry (Docker Hub by default).
- Registry: Authenticates the user if needed.
- Registry: Sends metadata and layers to the daemon.
- Daemon: Stores the image locally.
Flow when you use docker push:
- Daemon: Sends image layers to the registry.
- Registry: Stores layers and tags them.
Naming convention:
[registry-host/]namespace/repository[:tag]
Examples:
| Example image name | Meaning |
|---|---|
nginx | library/nginx:latest on default registry |
nginx:1.25 | nginx 1.25 tag on default registry |
myuser/myapp:1.0 | User myuser repository myapp on default registry |
ghcr.io/myorg/api:prod | Hosted on GitHub Container Registry for myorg |
Registries are critical in backend deployment because they are the bridge between your build pipeline and your production servers.
Storage: Volumes and Bind Mounts
Docker needs to store:
- Image layers.
- Container writable layers.
- Application data.
By default, the writable layer inside a container disappears when the container is removed. For persistent data you use:
- Volumes
Managed by Docker, usually stored under/var/lib/docker/volumes/...on the host. - Bind mounts
Directly mount a host directory or file into the container.
These are not separate daemon processes, but conceptually they form a storage layer in the architecture.
Example: named volume
docker volume create mydata
docker run -d \
-v mydata:/var/lib/postgresql/data \
--name mydb postgres:16Example: bind mount (useful in development)
docker run --rm -it \
-v $(pwd):/app \
-w /app \
python:3.12-slim python main.pyArchitectural idea:
- Containers are ephemeral.
- Volumes and host directories hold long lived data.
Networking and Communication
From an architecture perspective, Docker networking solves two main needs:
- Containers talking to each other.
- Containers exposing services to the outside world.
Docker daemon manages different network drivers. Three common ones:
| Network type | Default name | Typical use |
|---|---|---|
| bridge | bridge | Default for standalone containers |
| host | host | Container shares host network namespace |
| none | none | No network connectivity |
With user defined bridge networks, Docker also provides:
- Automatic DNS resolution by container name.
- Isolated network segments.
Example:
docker network create backend-net
docker run -d --name db --network backend-net postgres:16
docker run -d --name api --network backend-net my-api:1.0
Inside api container, connecting to db:5432 works because Docker provides an internal DNS entry.
To expose a container to the outside, the daemon sets up port mappings:
docker run -d -p 8080:80 --name web nginxHere:
- Port 80 inside the container is reachable on port 8080 on the host.
- The daemon configures iptables or an equivalent mechanism to route traffic.
Putting It All Together: End to End Example
Consider a typical backend workflow.
You:
- Write code and a
Dockerfilefor your FastAPI application. - Run
docker build -t myorg/myapi:1.0 . - Run tests, then
docker push myorg/myapi:1.0to a registry. - On a production server, you run
docker pull myorg/myapi:1.0. - You start the app with
docker run -d -p 80:8000 myorg/myapi:1.0.
Under the hood:
- The client sends
build,push,pull, andruncommands. - The daemon:
- Builds the image from a stack of layers.
- Stores the image locally.
- Communicates with the registry.
- Creates a container from the image.
- Sets up networking on port 80.
- Manages storage for container data.
Every time you use Docker in backend development, you are using this architecture: a client talking to a daemon, which orchestrates images, containers, networks, volumes, and registries.
Understanding this structure will help you when you:
- Configure remote deployments.
- Debug networking and storage issues.
- Secure the Docker environment on production servers.
- Work with more advanced tools like Docker Compose and Kubernetes, which build on the same concepts.
Views: 6
KAHIBARO