Kubernetes Overview · Part 0606

Docker ArchitectureFrom the Docker CLI to containerd and runc

What actually happens after you type docker run nginx? What the Docker CLI, API, Docker Engine, BuildKit, volumes, security, containerd, containerd-shim and runc each do, explained simply.

Translated from the original Bangla with AI. If anything reads oddly, the Bangla version is the reference.

Docker architecture stack: docker run goes from the Docker CLI through the Docker API and Docker Engine to containerd, containerd-shim, runc and the Linux kernel
FIG. 06 One docker run command: CLI → API → Docker Engine → containerd → shim → runc → Linux kernel.

When we work with Docker, we usually run commands like docker run, docker build or docker compose up.

But have you ever wondered what actually happens after you type docker run nginx in the terminal?

How does Docker download an image? Who creates the container? Who starts the container's process? And what is the real difference between Docker Engine, containerd and runc?

Today we will try to understand Docker's whole architecture in simple terms.

What is Docker, really?

Many people think Docker simply means containers. But Docker is not just a tool for running containers.

Docker is a complete containerization platform that lets you build, package, distribute, run and manage applications.

Several important components work together in the Docker ecosystem.

Docker Platform
│
├── Docker CLI
│
├── Docker API
│
├── Docker Engine (dockerd)
│
├── Build Tools (BuildKit / Buildx)
│
├── Images & Registries
│
├── Networking
│
├── Volumes
│
├── Authentication & Authorization
│
├── Security
│
└── Container Runtime
    │
    ├── containerd
    │
    └── runc

Each of these components has its own responsibility.

From building an application's Docker image to running it as a container, different components each do their part.

Let's go through them one by one.

1. Docker CLI — How we talk to Docker

The Docker CLI (Command Line Interface) is the tool we use to give Docker instructions from the terminal.

For example:

docker run nginx

docker ps

docker build -t my-app .

docker compose up

docker logs container-name

Most of us use these all the time.

The interesting part is that the Docker CLI does not run containers itself.

The Docker CLI takes our command and sends a request to Docker Engine's API.

Say you run the following command:

docker run nginx

What does the Docker CLI do now?

It sends a request to the Docker API.

In plain words, the request says:

Create a new container from the nginx image and start it.

Docker Engine then processes that request.

The Docker CLI workflow

User
  │
  │ docker run nginx
  ▼
Docker CLI
  │
  │ API Request
  ▼
Docker Engine
  │
  ▼
Container Creation

In other words, the CLI is the interface for using Docker, not the container runtime.

Real-world example:

When you click a button on a website, the button itself does not update the database. It sends a request to the backend API.

The Docker CLI works in much the same way.

2. Docker API — Docker's communication layer

The Docker CLI can talk to Docker Engine thanks to the Docker API.

Docker Engine exposes an API that different tools can use to access Docker's functionality.

Not only the Docker CLI: other applications can use the Docker API too.

For example:

Docker CLI ──────────┐
                     │
Portainer ───────────┤
                     │
CI/CD Tools ─────────┼──► Docker API
                     │          │
Custom Scripts ──────┤          ▼
                     │     Docker Engine
                     │
Automation Tools ────┘

Portainer here is a graphical management tool for managing a Docker environment.

You can start or stop containers from Portainer's web interface because it can talk to the Docker API.

In the same way, GitLab CI, a self-hosted GitHub Actions runner or custom automation scripts can use the Docker API when they are configured to.

What can you do with the Docker API?

  • Create new containers
  • Start and stop containers
  • Pull Docker images
  • Build Docker images
  • List running containers
  • View a container's logs
  • Inspect Docker networks
  • Manage Docker volumes

What is the Docker socket?

On Linux, Docker Engine usually exposes a Unix socket.

/var/run/docker.sock

The Docker CLI uses this socket to talk to Docker Engine locally.

For example:

docker ps

When you run this command, the Docker CLI asks Docker Engine for information about running containers through the local Docker socket.

Docker socket security

There is an important security point here.

In a typical rootful Docker setup, access to the Docker socket means very powerful control over the host machine.

That is because the Docker API can be used to create privileged containers, mount host directories or access sensitive files.

So in production, you should not give just any container or user access to the Docker socket.

Be especially careful with configuration like this:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Mounting the Docker socket like this can make it possible to control the host's Docker Engine from inside that container.

3. Docker build tools — How is a Docker image made?

Before we can run a container, we need a Docker image.

Docker's build tools create that image.

The two key technologies in modern Docker image building are:

BuildKit: the image build execution engine.

Buildx: a plugin that gives the Docker CLI advanced build features.

Say you have a Node.js application.

You write a Dockerfile for it:

FROM node:22

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

CMD ["npm", "start"]

Now, to build the image:

docker build -t my-node-app .

What happens when you run this command?

Dockerfile
    │
    ▼
Docker Build
    │
    ▼
BuildKit
    │
    ├── Base Image Resolve
    │
    ├── Build Instructions Process
    │
    ├── Dependencies Install
    │
    ├── Build Cache Use
    │
    └── Image Layers Create
    │
    ▼
Final Docker Image

BuildKit runs the build process following the instructions in the Dockerfile.

Key features of the build tools:

  • Processing Dockerfile instructions
  • Downloading base images
  • Build caching
  • Creating image layers
  • Multi-stage builds
  • Build secrets support
  • Multi-platform builds
  • Optimizing the build process

Multi-stage Docker builds

In production, the dependencies you need to build an application are often not needed at runtime.

A multi-stage Dockerfile is very useful here.

Say you have a frontend application:

# Stage 1: Build Application

FROM node:22 AS builder

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

# Stage 2: Production Runtime

FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html

The first stage builds the application with Node.js.

The second stage copies only the generated static files into the Nginx image.

So the final image does not need to carry the Node.js build dependencies.

Benefit: the image is smaller, there are fewer unnecessary build dependencies, and production deployment is simpler.

4. Docker volumes — How does data survive when a container is deleted?

Docker containers are generally designed to be replaceable.

Say you are running a PostgreSQL database inside a container.

What happens to the database's data when you remove the container?

If the data lives only in the container's writable filesystem, it can be lost when the container is deleted.

The solution to this problem is Docker volumes.

A Docker volume keeps persistent data separate from the container's lifecycle.

Example: a PostgreSQL database

docker run -d \
  --name postgres-db \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql \
  postgres:18

Here:

pgdata

is a Docker named volume.

And:

/var/lib/postgresql

is the data storage location of the PostgreSQL 18 image.

Note: the recommended data directory can change between PostgreSQL image versions. For older PostgreSQL images, /var/lib/postgresql/data is the usual path.

How does a volume work?

Host Machine
│
├── Docker Engine
│   │
│   └── PostgreSQL Container
│          │
│          └── Database Files
│                 │
│                 ▼
└── Docker Volume: pgdata
          │
          └── Persistent Storage

A named volume usually remains even after the container is removed.

So if you mount the same volume into a new, compatible PostgreSQL container, the earlier data is still available.

Of course, you still need to think about database version compatibility and a proper backup strategy.

Named volume vs bind mount

The two common approaches to persistent storage in Docker are:

FeatureNamed VolumeBind Mount
Managed byDockerHost filesystem
Storage pathDocker managesUser specifies
Common useDatabase, persistent dataDevelopment files, configuration
PortabilityDocker-managed storageDepends on the host path

Named volume example

docker run \
  -v pgdata:/var/lib/postgresql \
  postgres:18

Bind mount example

docker run \
  -v /home/user/app:/app \
  node:22

A bind mount connects a directory on the host machine directly to a directory in the container.

This is very useful in development, because it makes your local source code available inside the container.

Where are volumes used?

Volumes are used for database storage, uploaded files, persistent application state and some log storage scenarios.

But in production, having a volume does not by itself mean the data is safe. You need a separate backup and recovery process.

5. Docker authentication & authorization — Who can access what?

Docker authentication comes up most often when using a Docker registry.

A Docker registry is where Docker images are stored and distributed.

For example:

  • Docker Hub
  • GitHub Container Registry (GHCR)
  • GitLab Container Registry
  • Private Enterprise Registry

Public images can usually be pulled without logging in.

But accessing private images may require credentials.

Docker login

docker login

To log in to a specific registry:

docker login ghcr.io

An example of pulling a private image:

docker pull ghcr.io/company/private-app:latest

To push an image:

docker push myrepo/my-app:latest

The image name, credentials and registry permissions all have to be correct.

Authentication vs authorization

These are not the same thing.

Authentication: who are you?

Authorization: what are you allowed to do?

For example:

Developer
    │
    ▼
Registry Login
    │
    ▼
Authentication
    │
    ▼
Permission Check
    │
    ├── Pull Image: Allowed
    │
    └── Push Image: Denied

Here, a developer may be able to log in to the registry but still not have permission to push images.

Why does auth matter in production?

Enterprise environments typically use private container registries, CI/CD service accounts, credential helpers and scoped access tokens.

Credentials should never be hardcoded in a Dockerfile.

In CI/CD environments, it is best to use secrets management and least-privilege access.

One more thing to remember: registry authentication and local access control to the Docker daemon are two separate things.

6. Docker security — How isolated are containers?

Docker containers do not run a completely separate operating system the way virtual machines do.

Linux containers usually share the host machine's Linux kernel.

That is why container isolation and security matter so much.

Docker uses a range of Linux kernel security features.

Security FeatureWhat it does
NamespacesIsolates processes, network, mounts, users and so on
cgroupsControls CPU, memory and resource usage
Linux CapabilitiesLimits privileged operations
SeccompFilters system calls
AppArmorEnforces security profiles
SELinuxMandatory access control policies
User NamespacesMaps container user IDs separately from the host's IDs
Read-only FilesystemLimits write access to the container filesystem

Let's look at these a little more simply.

Namespaces

Namespaces help create an isolated environment for a container.

Say two containers are running on the same Linux server.

Linux Host
│
├── Container A
│   ├── Process Namespace
│   ├── Network Namespace
│   └── Mount Namespace
│
└── Container B
    ├── Process Namespace
    ├── Network Namespace
    └── Mount Namespace

The processes and network environment inside Container A can be kept separate from Container B.

cgroups

With cgroups, you can control how much CPU or memory a container can use.

Memory limit

docker run --memory=512m nginx

Here the container's memory limit is 512 MB.

CPU limit

docker run --cpus=1 nginx

Here the container is configured to use at most roughly one CPU core's worth of CPU time.

These limits matter for resource management in shared server environments.

Read-only filesystem

docker run --read-only nginx

This option makes the container's root filesystem read-only.

But if the application needs to write files at runtime, you have to configure separate writable mounts or tmpfs. Even the default Nginx configuration can run into problems with this option unless extra writable paths are provided.

Non-root user

Running the container's process as a non-root user is good security practice.

docker run --user 1000:1000 my-app

The application's file permissions and runtime requirements have to be compatible with it, though.

Privileged containers — Why are they risky?

docker run --privileged ubuntu

--privileged can give a container many extra host capabilities and device access.

That weakens container isolation.

So this option should not be used unless there is a specific need.

The core idea of production security

Give a container only as much permission as it needs.

This is called the Principle of Least Privilege.

7. containerd — The container lifecycle manager

Now we come to an important part of Docker's architecture.

When we run:

docker run nginx

Docker Engine does not do all of the work of creating the Linux process directly.

Docker Engine uses a container runtime layer underneath it.

The key component of that layer is containerd.

containerd is an industry-standard container runtime that manages the container lifecycle and related runtime operations.

Its responsibilities include:

  • Managing container images
  • Managing image content and snapshots
  • Container lifecycle operations
  • Managing runtime tasks
  • Integrating with the lower-level OCI runtime

How Docker and containerd relate

Docker CLI
     │
     ▼
Docker API
     │
     ▼
Docker Engine (dockerd)
     │
     ▼
containerd
     │
     ▼
containerd-shim
     │
     ▼
runc
     │
     ▼
Container Process

Here, containerd-shim is an important runtime component that plays a role in the container process's lifecycle and I/O management.

containerd in Kubernetes

containerd is not only for Docker.

It is also a very commonly used container runtime in Kubernetes.

In modern Kubernetes architecture, Kubernetes usually talks to compatible runtimes through the CRI (Container Runtime Interface).

A simplified flow:

Kubernetes API Server
          │
          ▼
       Kubelet
          │
          ▼
        CRI
          │
          ▼
      containerd
          │
          ▼
     containerd-shim
          │
          ▼
         runc
          │
          ▼
     Linux Container

One important point: the built-in dockershim was removed in Kubernetes v1.24.

So a Kubernetes node can use containerd directly as its container runtime, without Docker Engine.

Docker and containerd are not competitors. Docker is a broader container platform, and containerd is a container runtime component.

8. runc — The component that creates the actual container process

At an even lower layer of Docker's architecture sits runc.

runc is a low-level, OCI-compatible container runtime.

OCI stands for: Open Container Initiative.

OCI standards help ensure interoperability of container images and runtime behavior.

runc's main job is to create and start the container process using the Linux kernel's capabilities.

Its work includes:

  • Configuring Linux namespaces
  • Applying cgroups configuration
  • Setting up the container filesystem
  • Applying security settings
  • Starting the container's initial process

How does runc work?

Say you have run the following command:

docker run nginx

The simplified flow is then:

docker run nginx
       │
       ▼
Docker CLI
       │
       ▼
Docker API
       │
       ▼
Docker Engine
       │
       ▼
containerd
       │
       ▼
containerd-shim
       │
       ▼
runc
       │
       ▼
Linux Kernel Features
       │
       ▼
Nginx Container Process

Here, runc creates the container process according to the OCI runtime configuration.

After the container starts, runc usually does not stay around as a long-running process. containerd-shim takes on the role of supervising the running container's lifecycle.

This distinction matters, because containerd and runc do not do the same job.

containerd: higher-level runtime lifecycle management.

runc: low-level container process creation.

9. Docker's full architecture — The complete picture

Now let's try to understand all the components together.

                  DEVELOPER
                      │
                      ▼
                 Docker CLI
                      │
                      ▼
                  Docker API
                      │
                      ▼
               Docker Engine
                  (dockerd)
                      │
       ┌──────────────┼──────────────┐
       │              │              │
       ▼              ▼              ▼
 BuildKit /       Networking     Volumes /
  Buildx          Management      Storage
       │              │              │
       └──────────────┼──────────────┘
                      │
                      ▼
                 containerd
                      │
                      ▼
               containerd-shim
                      │
                      ▼
                    runc
                      │
                      ▼
                 Linux Kernel
                      │
                      ▼
                Running Container

This is a simplified, conceptual architecture.

In reality, image building, registry access, networking, storage and runtime operations do not all happen in one sequential pipeline. Docker Engine coordinates several subsystems.

10. A real example — What happens inside docker run nginx?

Now let's walk through a complete example.

Say you type this in the terminal:

docker run -d -p 8080:80 nginx

Running this one command sets off many operations inside Docker.

Step 1: The Docker CLI receives the command

The CLI parses the command's parameters.

Here:

  • -d means detached mode
  • -p 8080:80 means forwarding traffic from host port 8080 to container port 80
  • nginx is the image name

Step 2: A request goes to the Docker API

The Docker CLI sends a request to Docker Engine's API to create a container.

Step 3: Docker Engine checks for the image

Docker Engine checks whether the Nginx image is available locally.

If it is not, it arranges to pull it from the registry.

Step 4: Registry authentication may be needed

The public Nginx image can usually be pulled anonymously.

For an image in a private registry, the appropriate credentials are needed.

Step 5: The container configuration is prepared

Docker Engine prepares the configuration for the container.

For example:

  • Container filesystem
  • Networking
  • Port publishing
  • Environment variables
  • Volume mounts
  • Security configuration
  • Resource limits

Step 6: containerd manages the container task

Docker Engine talks to containerd to coordinate the runtime operations.

Step 7: runc creates the container process

runc is invoked through containerd-shim.

runc uses the Linux kernel's namespaces, cgroups and other isolation features to create and start the container process.

Step 8: The Nginx process starts

Nginx is now running as a Linux process inside the container.

Step 9: Port mapping kicks in

Docker sets up forwarding of incoming connections from port 8080 on the host machine to port 80 in the container.

Step 10: The application is reachable from the browser

Now open a browser and go to:

http://localhost:8080

and you will see the Nginx welcome page.

Complete runtime flow

User
 │
 │ docker run -d -p 8080:80 nginx
 ▼
Docker CLI
 │
 ▼
Docker API
 │
 ▼
Docker Engine
 │
 ├── Check Image
 │
 ├── Pull Image if Required
 │
 ├── Prepare Networking
 │
 ├── Configure Storage
 │
 ├── Apply Security Settings
 │
 ▼
containerd
 │
 ▼
containerd-shim
 │
 ▼
runc
 │
 ▼
Linux Kernel
 │
 ▼
Nginx Process
 │
 ▼
Host Port 8080 → Container Port 80
 │
 ▼
http://localhost:8080

In other words, many technologies work together behind one simple Docker command.

11. Docker architecture — A simple real-life analogy

A simple example helps you remember Docker's components.

Imagine Docker is a big company.

Different departments in this company do different jobs.

Docker ComponentReal-Life Analogy
Docker CLIReception Desk
Docker APIInternal Communication System
Docker EngineOperations Director
BuildKit / BuildxFactory
ImagesPackaged Products
RegistryWarehouse / Distribution Center
NetworkingCommunication Infrastructure
VolumesPermanent Storage Room
AuthenticationIdentity Verification
AuthorizationAccess Permission System
SecurityRules, Locks and Restricted Areas
containerdOperations Manager
containerd-shimContainer Supervisor
runcThe worker who starts the container
Linux KernelCore Infrastructure

Example

Say you walk up to the company's reception and say:

Please start a new Nginx server for me.

The reception desk, that is the Docker CLI, takes your request.

The internal communication system, that is the Docker API, passes the request to the operations director, Docker Engine.

Docker Engine coordinates preparing the image, networking and storage that are needed.

Then containerd manages the runtime operations.

runc starts the actual container process with the help of the Linux kernel.

Finally, the Nginx server starts running.

That is how each Docker component carries out its own responsibility.

12. Docker's most important components — Quick reference

ComponentMain job
Docker CLITakes user commands and sends API requests
Docker APICommunication between external tools and Docker Engine
Docker EngineCoordinates Docker resources and container operations
BuildKitBuilds Docker images efficiently
BuildxProvides an advanced image building interface
Docker ImagesThe packaged application a container runs from
RegistryStores and distributes Docker images
NetworkingContainer communication and port publishing
VolumesStores persistent application data
AuthenticationVerifies user or service identity
AuthorizationControls which operations are permitted
SecurityContainer isolation and privilege restrictions
containerdManages the container runtime lifecycle
containerd-shimHelps supervise running containers
runcCreates the container process from the OCI configuration
Linux KernelProvides process execution, isolation and resource control

13. Docker Engine vs containerd vs runc — The real difference

To understand Docker's architecture, you need to be clear on the difference between these three components.

Docker Engine

Docker Engine is a higher-level container management platform.

It manages images, containers, networks, volumes and the Docker API.

Its big advantage is a developer-friendly interface and workflow.

containerd

containerd is the container runtime management layer.

It handles image content, snapshots and container lifecycle operations.

It can be used in both the Docker and Kubernetes ecosystems.

runc

runc is a low-level OCI runtime.

It uses the Linux kernel's isolation mechanisms to create and start the actual container process.

Comparison

FeatureDocker Enginecontainerdrunc
Abstraction LevelHighMedium / LowLow
Primary ResponsibilityContainer Platform ManagementContainer Runtime ManagementContainer Process Creation
Typical UsersDevelopers, DevOpsRuntime / Platform EngineersRuntime Implementations
CLIdockerctr, nerdctlrunc
Kubernetes RuntimeNot directly via built-in dockershimYes, through CRI integrationUsed underneath compatible runtimes
Image BuildingBuildKit integrationNot a full Docker build platformNo

14. Final takeaway — Docker is really a complete technology stack

The easiest way to understand Docker's architecture is to remember each component's responsibility.

Docker CLI
    → The commands we use

Docker API
    → How tools talk to Docker Engine

Docker Engine
    → Manages Docker resources and operations

BuildKit
    → Builds Docker images

Volumes
    → Stores persistent data

Authentication
    → Verifies identity

Authorization
    → Controls access permissions

Security
    → Applies container isolation and restrictions

containerd
    → Manages the container runtime lifecycle

containerd-shim
    → Supervises running containers

runc
    → Creates and starts the actual container process

Linux Kernel
    → The foundation of container execution and isolation

The most important point: Docker does not create a new operating system. Linux containers run as isolated processes using the host's kernel features.

To run Linux containers on macOS and Windows, Docker Desktop usually uses a Linux virtual machine or a WSL 2 environment.

So a Docker container and a virtual machine are not the same thing.

A container usually runs in a shared kernel environment, while a virtual machine runs its own guest operating system and kernel.

This architectural difference is what makes containers lightweight and quick to deploy for many workloads.

Official References

To learn more about Docker's internal architecture, read the documentation below.

Final words

When we type docker run nginx, the whole thing looks very simple from the outside.

But behind it, the Docker CLI, the API, Docker Engine, containerd, containerd-shim, runc and the Linux kernel all work together.

For a developer, knowing Docker commands is important. But if you want to work in DevOps, platform engineering, cloud infrastructure or Kubernetes, understanding Docker's internal architecture matters even more.

Because then you will not only be able to run containers, you will also be able to troubleshoot systematically: why a container is not starting, where the runtime is failing, and where a networking or storage problem lies.

Docker is not just a tool for running containers — it is a complete platform for application build, distribution and container lifecycle management.

Was this guide helpful?

Search the archive

Press Esc to close