The Docker Engine

The Docker Engine

A friendly, step-by-step tour of the software that builds, ships and runs containers: what is inside it, how it works, how to keep it safe and healthy, and how real companies use it every day.

Picture this. You finish building an app on your laptop, and it runs perfectly. You send it to a teammate, or to a server in the cloud, and it crashes. A library has a different version. A setting is missing. The operating system is not the same. Developers have joked about this for decades: “But it works on my machine!”

The Docker Engine was built to end that joke. It lets you pack an app together with everything it needs into a sealed, portable box called a container, and then run that box the same way on any computer that has the engine installed. In this guide you will learn what the Docker Engine is, what lives inside it, how it works step by step, how to keep it safe, and how companies such as Netflix and Amazon use the same ideas at huge scale. We explain every new word the first time it appears, so you can read from top to bottom without any earlier knowledge.

1What Is the Docker Engine?

The engine is the working heart of Docker. Everything else, from the laptop app to the cloud services, sits on top of it.

The Docker Engine is the software that creates and runs containers on a computer. A container is an app plus all the files, libraries and settings it needs, wrapped up so that it runs in its own private space. The app inside believes it has the whole computer to itself, even though many containers may be sharing one machine.

The engine is a client-server program made of three parts. The first is a long-running background program called the daemon, whose command name is dockerd. A daemon is simply a program that keeps running quietly in the background and waits for work. The second is an API, which is a set of agreed messages that other programs can send to the daemon to ask for things. The third is the command-line client, the docker command you type in a terminal. When you type a command, the client turns it into an API message and sends it to the daemon, and the daemon does the real work.

Everyday analogy

Think of the steel shipping containers stacked on cargo ships. Before they existed, workers loaded sacks, barrels and crates one by one, and every port did it differently. Once everyone agreed on one standard box size, any crane, truck, train or ship could move any box without caring what was inside. Docker did the same thing for software. The container is the standard box. The Docker Engine is the crane that knows how to lift it, place it and open it.

The problem it was created to solve

Before containers, teams usually installed apps directly on servers. Two apps on the same server might need different versions of the same library, and they would fight each other. Moving an app from a developer laptop to a test server to a production server meant repeating a long list of setup steps, and someone always forgot one. Virtual machines helped, but they were heavy and slow to start. Containers gave teams a lighter, faster way to get the same repeatable result.

2013
year the project was first shown publicly
Go
programming language the engine is written in
Open source
the engine grew from the Moby project under a permissive license

A short history you can remember

2013

Docker appears

A company then called dotCloud shows Docker at a Python conference. It builds on Linux features that already existed but makes them easy for everyday developers to use.

2014

Its own engine and version 1.0

Docker replaces its early dependency on LXC, an older container tool, with its own Go library, and reaches its first stable release.

2015

An open standard is born

The Open Container Initiative is formed so that the container format is not owned by one company. Docker donates its low-level runtime code, which becomes runc.

2016

The engine is split into parts

The engine is reorganised around containerd and runc, and a built-in clustering feature called swarm mode arrives.

2017

Moby and containerd go community-owned

The Moby project is announced as the open-source home of the engine’s building blocks, and containerd moves to the Cloud Native Computing Foundation.

2022

Kubernetes stops talking to Docker directly

Kubernetes removes its old Docker adapter and uses containerd or similar runtimes. Images built with Docker keep working because they follow the open standard.

2023

A modern builder becomes the default

BuildKit becomes the default image builder in the engine, making builds faster and smarter.

i
Three names people mix up

Docker Engine is the open-source runtime described in this guide. Docker Desktop is a desktop application for Mac, Windows and Linux that bundles the engine with a small hidden Linux virtual machine and extra tools. Docker Inc. is the company that steers the project and sells the desktop app and other services.

Why the ecosystem around it grew so quickly

Part of the reason Docker spread so fast is that it launched alongside Docker Hub, a public registry where anyone could publish a ready-made image for free. Instead of writing installation steps for a database or a web server from scratch, a developer could simply download an image someone else had already built, tested and shared. Within a few years, official images existed for almost every popular piece of software, and companies began publishing their own images for internal reuse. This network effect, an easy engine paired with a huge shared library of images, is a large part of why containers moved from a niche developer tool to something used by nearly every technology company within about a decade of that first public demonstration.

2Containers vs Virtual Machines

To understand why containers are popular, compare them with the older idea they often replace.

A virtual machine, or VM, is a whole pretend computer running inside a real one. A special layer of software called a hypervisor carves a physical server into several virtual servers. Each VM carries its own full operating system, its own memory and its own virtual hardware. That gives strong separation, but each VM may need several gigabytes of disk space and can take from tens of seconds to a few minutes to boot.

A container skips the pretend computer. All containers on a host share the kernel of the host’s operating system. The kernel is the core part of an operating system that talks to the hardware and decides which program gets the processor, memory and disk. Because there is no second operating system to boot, a container is usually just a few megabytes to a few hundred megabytes and can start in well under a second to a few seconds. A tiny base image such as Alpine Linux is only around five megabytes.

Everyday analogy

A virtual machine is like giving every family their own separate house, complete with its own foundation, pipes and wiring. A container is like an apartment in one big building. Every apartment has its own locked door, its own rooms and its own furniture, but they all share the same foundation, the same water supply and the same electrical system. Apartments are cheaper and quicker to set up. Houses give stronger walls between neighbours.

QuestionVirtual machineContainer
What is inside?A full operating system plus your appYour app plus its libraries only
Typical sizeGigabytesMegabytes to a few hundred megabytes
Start-up timeTens of seconds to minutesUnder a second to a few seconds
KernelEach VM has its ownShared with the host
Isolation strengthVery strongGood, but weaker because the kernel is shared
How many fit on one serverTensHundreds or more
Best forRunning different operating systems, strict separationPackaging and scaling many small services quickly
!
Gotcha for Mac and Windows users

Linux containers need a Linux kernel. On a Mac or Windows laptop, Docker Desktop quietly starts a small Linux VM and runs your containers inside it. That is why file sharing between your laptop and a container can feel slower there than on a real Linux server.

The best of both worlds

Containers and virtual machines are not enemies. Most cloud companies use both. Amazon’s AWS Fargate, for example, runs each customer task inside its own lightweight virtual machine built with a technology called Firecracker. The customer still thinks in containers, while AWS gets the strong wall of a VM between different customers. You will see this idea of layering again in the security chapter.

3Core Concepts You Must Know

Eight small ideas explain almost everything in Docker. Learn these words and the rest becomes easy to follow.

Blueprint

Image

A read-only package holding your app, its libraries and a minimal set of operating system files. Think of it as a frozen recipe card plus all the pre-measured ingredients.

Running thing

Container

A live, running copy made from an image. If the image is the recipe, the container is the cake that comes out of the oven. You can bake many cakes from one recipe.

Instructions

Dockerfile

A plain text file listing the steps to build an image, in order. It works like the numbered steps on a flat-pack furniture leaflet.

Building block

Layer

Each build step adds one layer of file changes. Layers stack like transparent sheets on an overhead projector, and the final picture is what you see through all of them.

Library

Registry

An online storage place for images, such as Docker Hub or Amazon ECR. It works like a public library or app store where you borrow images and return new ones.

Names

Tag and digest

A tag is a friendly, movable label such as “1.2”. A digest is a long, fixed fingerprint of the exact image contents. A tag is a name on a sticky note; a digest is a fingerprint.

Storage

Volume

A storage area managed by Docker that lives outside the container, so data survives when the container is deleted. It is like a storage unit you rent next to your apartment.

Connections

Network

A virtual network that lets containers talk to each other and to the outside world, like the corridors and front door of an apartment building.

Image versus container, the most important pair

Beginners often mix these up, so let us be very clear. An image never changes once it is built. It is only a file collection sitting on disk. When you ask the engine to run an image, it adds a thin writable layer on top and starts a process. That running package is the container. Delete the container and the image is still there, unchanged, ready to make a new one.

Programmers may recognise this pattern: an image is like a class, and a container is like an object created from that class. If you do not write code, the recipe-and-cake picture works just as well.

✓
Tag vs digest

Tags can be moved. Today the tag “latest” might point to one image, and next month to a completely different one. A digest never moves, because it is calculated from the image content itself. When you need to be sure you are running exactly the same thing everywhere, use the digest.

4Architecture and Components

The engine is not one big lump. It is a chain of small programs, each with one clear job.

When you type a Docker command, your request travels through a short chain of programs before a container appears. The diagram below shows the chain from left to right.

flowchart LR
    CLI["Docker CLI: the docker command"] -->|"REST API calls"| API["Engine API: Unix socket or secured TCP"]
    API --> D["dockerd: the Docker daemon"]
    D --> BK["BuildKit: the image builder"]
    D --> NET["Networking: bridges and firewall rules"]
    D --> VOL["Volumes: persistent storage"]
    D -->|"gRPC"| C["containerd: container supervisor"]
    C --> SH["containerd-shim: one per container"]
    SH --> R["runc: OCI runtime"]
    R --> P["Your app process inside the container"]
    D -->|"pull and push images"| REG[("Registry: Docker Hub or Amazon ECR")]
Figure 1: From the docker command to a running app process

Reading the diagram

On the left, the Docker CLI (command-line interface) is the program you type into. It does almost no real work. It packages your request and sends it over the Engine API. By default this message travels through a local Unix socket, which is a special file that programs on the same machine use to talk to each other. The engine can also listen on a network port, but that must be protected with encryption and login checks, as you will see in the security chapter.

The daemon, dockerd, is the manager. It keeps track of images, containers, networks and volumes. It also talks to registries when images must be downloaded or uploaded. But the daemon does not start containers by itself. It hands that job down the chain to a helper called containerd.

containerd (say “container-dee”) is a container supervisor. It pulls image content, stores it in a way the system can use, and manages the life of every container. It is an independent project that Kubernetes also uses directly. For each container it launches a tiny program called the shim. The shim stays alive as long as the container does. It keeps the container’s input and output connected and remembers the exit code when the container stops. Thanks to the shim, you can restart the daemon for an upgrade while containers keep running, if the live-restore setting is on.

runc is the worker at the very end. It follows the Open Container Initiative runtime standard. It asks the Linux kernel to build the private space, applies the safety limits, and starts your app. Once the app is running, runc’s job is done and it exits.

Everyday analogy

Imagine a busy restaurant. The Docker CLI is the waiter who takes your order. The Engine API is the order slip. The daemon is the restaurant manager who decides what needs to happen and checks the pantry. containerd is the kitchen supervisor who assigns each dish. The shim is the assistant who stays beside each dish until it is served. And runc is the chef who actually cooks it, then steps away.

Other pieces around the core

Builder

BuildKit

Turns a Dockerfile into an image. It can run independent steps at the same time and remember earlier results to save time.

Plugins

Buildx and Compose

Add-on commands. Buildx builds for several processor types at once, and Compose starts several containers together from one file.

Clustering

Swarm mode

A built-in way to join several machines into one group and spread containers across them. Many teams now prefer Kubernetes for this job.

Storage

Storage drivers

The part that stacks image layers into one working file system. The default on modern Linux is called overlay2.

One more point matters for the rest of this guide. Every link in the chain follows an open standard: the OCI image and runtime specifications. That is why an image built by Docker can run under containerd, Podman or Kubernetes, and why you are never locked into a single tool.

5How Isolation Works Inside the Kernel

Containers feel like separate computers, but the trick is done with three ordinary Linux features working together.

A container is not magic. It is a normal Linux process that the kernel has been told to treat specially, using three ingredients: namespaces, control groups, and a security layer.

Namespaces: giving each container its own view

A namespace is a Linux feature that gives a process its own private view of one type of resource, while hiding everything else on the machine. There are several kinds. The PID namespace makes a container see only its own processes, so its first process looks like process number 1, even though the host sees it as, say, process number 4522. The network namespace gives the container its own private network card, IP address and routing table. The mount namespace gives it its own view of the file system, so it cannot see the host’s files unless they are deliberately shared. There are also namespaces for host names, for inter-process communication, and for user ID numbers.

Control groups: sharing resources fairly

Control groups, usually shortened to cgroups, are a Linux feature that limits and measures how much processor time, memory and disk activity a process may use. Without cgroups, one busy container could use all the memory on a server and starve every other container next to it. With a memory cgroup limit set, the kernel simply stops that container from allocating more, and the container may be safely stopped and restarted instead of freezing the whole machine.

A security layer on top

On top of namespaces and cgroups, the engine can apply extra rulebooks such as seccomp, which blocks dangerous low-level requests a container is allowed to make to the kernel, and AppArmor or SELinux, which restrict what files and actions a container’s processes may touch. These are covered in more depth in the security chapter.

Everyday analogy

Namespaces are like giving each tenant of an apartment building their own numbered mailbox and their own door key, so they only see their own mail and their own rooms. Control groups are like the building’s electrician setting a fuse for each apartment, so no single apartment can trip the power for the whole building by running too many appliances at once.

!
A common myth

Containers are not tiny virtual machines. They share the host’s actual kernel. This is exactly why they are fast and light, and also why isolation between containers is weaker than between virtual machines — a serious flaw in the shared kernel could, in theory, be used to see or affect another container. The security chapter explains how real deployments manage this risk.

6Images and Layers

Understanding layers explains why Docker builds and downloads are usually so fast after the first time.

An image is built from a text file called a Dockerfile. Each instruction in that file, such as copying in your code or installing a package, produces one read-only layer. The final image is simply an ordered stack of these layers, plus a small file called a manifest that lists them.

flowchart TB
    L1["Layer 1: base operating system files"] --> L2["Layer 2: system packages installed"]
    L2 --> L3["Layer 3: application dependencies"]
    L3 --> L4["Layer 4: application source code"]
    L4 --> L5["Writable container layer: created only when a container starts"]
Figure 2: Read-only layers stacked underneath one writable layer

Layers are stored once and reused. If ten different images all start from the same base layer, that layer is downloaded and stored on disk only once, and every image simply points to it. This is why pulling a second image that shares a base with one you already have often finishes in seconds instead of minutes.

Layers are also the reason build order matters. Docker checks whether an instruction and the files it depends on have changed since the last build. If nothing changed, it reuses the old layer instantly instead of redoing the work; this is called the build cache. Because of this, experienced teams place instructions that rarely change, like installing system packages, near the top of the Dockerfile, and instructions that change constantly, like copying in the latest source code, near the bottom. That way, an ordinary code change only rebuilds the last one or two layers.

How a container’s writable layer works

When a container starts, the engine adds one thin, writable layer on top of the image’s read-only layers, using a technique called copy-on-write. If the container’s process wants to change a file that exists in a lower, read-only layer, the storage driver copies that file up into the writable layer first, then changes the copy. The original layer, and every other container sharing it, is untouched. This is exactly why deleting a container does not affect the image or any other container built from it, and why many containers can share one image’s files in memory and on disk at the same time.

Everyday analogy

Layers are like tracing paper sheets stacked on top of a printed map. The bottom sheets are locked and shared by everyone. Your own sheet on top is the only one you may draw on. If you want to change a road that is printed on a locked sheet, you first trace a fresh copy of just that road onto your own sheet, then edit your copy. The original map underneath never changes.

One image, many processor types

Modern images can also be multi-architecture, meaning a single image name actually points to several different builds behind the scenes, one for each processor family such as the common Intel and AMD design or the Arm design used in many phones and newer laptops. When you pull an image, the engine automatically selects the correct build for your own machine, using a small index file called a manifest list. This is what lets the same docker pull command work correctly whether it is run on a traditional data-centre server or on a newer Arm-based cloud instance, without the person typing the command needing to know or care which processor family is underneath.

7Data Flow and Lifecycle

Follow one command from your keyboard all the way to a running, and later a stopped, container.

sequenceDiagram
    participant U as User
    participant CLI as Docker CLI
    participant API as Engine API
    participant D as dockerd
    participant C as containerd
    participant R as runc
    U->>CLI: docker run myapp
    CLI->>API: POST create + start container
    API->>D: forward request
    D->>D: check image locally, pull if missing
    D->>C: create and start task
    C->>R: run OCI bundle
    R->>R: build namespaces and cgroups
    R-->>C: process started, runc exits
    C-->>D: container running
    D-->>API: success
    API-->>CLI: container ID
    CLI-->>U: prints container ID
Figure 3: The full round trip of a single docker run command

When you run docker run myapp, the CLI first checks whether an image called myapp already exists on your machine. If not, the daemon downloads, or pulls, it from a registry, layer by layer, skipping any layers already present locally. The daemon then asks containerd to create a container from that image, containerd asks runc to actually start the process inside fresh namespaces and cgroups, and runc exits once the process is running, leaving the shim to watch over it.

The states a container passes through

1

Created

The container exists with its file system prepared, but its main process has not started yet.

2

Running

The main process is active. This is the normal working state.

3

Paused

All processes are temporarily frozen in memory using a cgroup freezer, without being stopped.

4

Stopped or exited

The main process has ended, either normally or because it crashed, and its exit code is recorded.

5

Removed

The container’s writable layer and metadata are deleted. Anything not saved to a volume is now gone for good.

✓
Good habit

Any file your app writes inside the container’s own writable layer disappears when the container is removed. Anything you truly need to keep, such as a database’s files or uploaded content, should be written to a volume instead, which is covered in the storage chapter.

8Networking Between Containers

Containers need to talk to each other and to the outside world. The engine gives each container its own virtual network card and several ways to connect them.

By default, the engine creates a private virtual network called bridge on the host. Every new container that does not ask for something different is attached to this bridge and given its own private IP address, similar to how a home Wi-Fi router hands out addresses to phones and laptops. Containers on the same bridge can reach each other by IP address, and if you create your own named bridge network, they can also reach each other simply by container name, because the engine runs a small internal DNS server for you.

Default

Bridge network

A private virtual network on one host. Good for most single-machine setups.

Performance

Host network

The container shares the host’s own network stack directly, skipping translation for extra speed, at the cost of isolation.

Multi-host

Overlay network

A virtual network that spans several physical machines, used by swarm mode and similar cluster tools.

Testing

None

The container gets no network access at all, useful for isolated batch jobs.

Reaching the outside world

A container’s private IP address is not reachable from outside the host by default. To let outside traffic in, you publish a port, for example telling Docker to send anything arriving on the host’s port 8080 to port 80 inside the container. Behind the scenes, the engine sets up network address translation rules using the host’s firewall so that traffic is quietly redirected to the right container.

Everyday analogy

The bridge network is like the internal phone extension system inside an office building; any desk can dial any other desk’s extension. Publishing a port is like assigning extension 205 to be reachable from an outside phone line dialled at the building’s public number, so a customer can call in without knowing the internal wiring.

A less common but useful option is the macvlan driver, which gives a container its own genuine hardware-level address on the physical network, making it appear to the rest of the building’s network as if it were a separate physical device rather than something hidden behind the host. This is handy for older applications that expect to own a real network address directly, but it is used far less often than the default bridge network, since it also means the host’s usual firewall protections no longer sit between the container and the network.

9Storage and Volumes

Containers are meant to be thrown away and rebuilt often, so anything that must survive needs a home outside the container.

A volume is a storage area managed entirely by the Docker Engine, stored on the host’s disk but kept outside any single container’s writable layer. When a container writes to a location where a volume is attached, that data is really being written into the volume, and it stays there even after the container is stopped, removed, or replaced by a newer version.

Recommended

Named volume

Fully managed by Docker, easy to back up, and the safest default choice for databases and other stateful apps.

Development

Bind mount

Maps a specific folder on the host straight into the container. Common for live-editing source code during development.

Speed

tmpfs mount

Stores data only in the host’s memory, never on disk. Fast, but everything is lost when the container stops.

!
A trap that surprises beginners

If you run a database inside a container without attaching a volume, every row you insert lives only in that container’s temporary writable layer. The moment someone removes that container to upgrade the image, all of that data is gone permanently. Always attach a volume for anything that must outlive the container.

Volumes can also be shared between several containers at once, which is how, for example, a web server container and a separate log-collecting container can both read and write the same log files without copying them back and forth.

Volume drivers and cloud storage

On a single machine, a volume is normally just a managed folder on the local disk. However, the engine also supports pluggable volume drivers, which let a volume actually be backed by something other than local disk, such as a network file share or a cloud storage service. This matters most once containers move across several machines in a cluster: if a container with important data is rescheduled from one server to another, a local-only volume would be left behind, but a volume backed by shared network or cloud storage can follow the container to its new home. This is one of the details orchestrators such as Kubernetes handle for you through their own storage systems, building on the same underlying volume idea introduced here.

10Advantages, Disadvantages and Trade-offs

No technology is free of trade-offs. Knowing the honest downsides makes you a stronger engineer, not a weaker one.

Advantages

  • Consistent behaviour from a laptop to a production server, because the same image runs everywhere
  • Fast start-up, often under a second, which suits scaling up and down quickly
  • Efficient use of hardware, since many containers share one kernel instead of each needing a full operating system
  • A huge ecosystem of ready-made images and tools built around one open standard
  • Clear packaging of an app’s exact dependencies, ending most “works on my machine” arguments

Disadvantages

  • Weaker isolation than a virtual machine, because the kernel is shared between containers
  • Linux containers need a Linux kernel, so Mac and Windows users run an extra hidden virtual machine
  • Running many containers well at scale usually needs an orchestrator such as Kubernetes, adding its own complexity
  • Persistent data needs deliberate planning with volumes, or it is easily lost by accident
  • Badly written images can be very large or contain outdated, vulnerable software if not maintained
“Containers do not remove complexity. They move it from configuring individual servers to designing images, networks and volumes with more care.”

11Security

Because containers share a kernel, security has to be built in layers rather than relied on as one single wall.

Built-in defences

Kernel filter

Seccomp profiles

Block a container from making dangerous, rarely needed requests directly to the kernel, shrinking the space an attacker could exploit.

Access rules

AppArmor / SELinux

Mandatory access control systems that restrict which files, network actions and capabilities a container’s processes may use, even if the app itself is compromised.

Privilege drop

Linux capabilities

Root inside a container normally has a reduced set of powers compared with real root on the host, and extra capabilities can be dropped further.

Non-root

User namespaces

Can remap root inside a container to an unprivileged, ordinary user ID on the host, so a break-out gains far less.

Practices every team should follow

  • Run the process inside the container as a non-root user whenever the application allows it.
  • Keep base images small and current, since fewer installed packages means fewer known vulnerabilities to patch.
  • Scan images for known vulnerabilities before they are deployed, using a vulnerability scanner as part of the build pipeline.
  • Sign and verify images so a team can trust that the image running in production is exactly the one that was built and tested.
  • Never mount the host’s own Docker socket into a container casually, since a process with access to it can effectively control every container on that host.
  • Protect the Engine API. If it must be reachable over the network, always require mutual TLS encryption and authentication rather than leaving it open.
PATTERN · DEFENCE IN DEPTHRECOMMENDED
The situation

A single shared kernel means one deep flaw could, in theory, let a process escape its container and affect its neighbours.

The response

Serious deployments stack several independent barriers rather than trusting one: non-root users, seccomp and capability restrictions, network policies that limit which containers may even talk to each other, and for the highest-risk workloads a stronger boundary such as a lightweight virtual machine per container, as AWS Fargate does with Firecracker.

Why it works

Even if an attacker defeats one layer, the remaining layers still have to be broken separately, which sharply raises the cost and difficulty of a successful attack.

!
A frequent real mistake

Teams sometimes run containers with the --privileged flag “just to make an error go away.” This flag removes almost all of the isolation described in this guide. It should be treated as a last resort for very specific, trusted tooling, never as a routine fix.

12Monitoring, Logging and Metrics

You cannot fix what you cannot see. The engine gives you basic visibility out of the box, and larger systems build much more on top.

Every container’s standard output and standard error, the two text streams a program normally prints to, are captured automatically by the engine through a logging driver. The default driver simply writes these streams to local files in JSON format, which the docker logs command reads back for you. In real production systems, teams usually switch to a driver that forwards logs straight to a central logging platform, so that logs from hundreds of containers across many servers can be searched in one place instead of one host at a time.

For live resource usage, the docker stats command shows processor use, memory use, network traffic and disk activity for running containers, pulled directly from the same cgroup counters the kernel already keeps. This is useful for a quick manual check, but production systems usually export these numbers continuously to a metrics platform such as Prometheus, so trends can be graphed and alerts can fire automatically when something drifts out of range.

SignalWhat it tells youTypical tool
LogsWhat the application said happened, in orderCentralised logging platform
MetricsNumeric trends over time, such as CPU or request countPrometheus and a graphing dashboard
Health checksWhether the app inside the container is actually working, not just runningDocker’s built-in HEALTHCHECK instruction
EventsLife-cycle changes such as a container starting, stopping or dyingdocker events stream
✓
Running versus healthy

A container can be in the “running” state while the application inside it is frozen or broken. A Dockerfile’s HEALTHCHECK instruction lets the engine periodically run a small test command inside the container, such as asking a web server for a page, and mark the container unhealthy if that test keeps failing, so automated systems can restart or replace it.

As the number of containers grows, teams usually add one more layer of visibility on top of logs and metrics: distributed tracing. A single user request in a modern application might pass through a dozen small services, each running in its own container, before a response is sent back. Tracing tools attach a shared identifier to that request as it travels, so that when something is slow or fails, an engineer can see the exact path the request took across every container involved, rather than searching through a dozen separate log files by hand and guessing which ones are related.

13Deployment and the Cloud

A single Docker Engine on one laptop is where most people start. Real products usually run across many machines, coordinated by an orchestrator.

An orchestrator is a system that decides which container runs on which machine, restarts containers that fail, and spreads traffic across many copies of the same app. Docker ships its own simple orchestrator, called swarm mode, built directly into the engine. Most large organisations instead choose Kubernetes, a separate, more feature-rich orchestrator that has become the industry standard, even though it no longer talks to the Docker Engine directly and instead uses containerd or a similar runtime underneath.

flowchart TB
    Dev["Developer writes Dockerfile"] --> Build["CI pipeline builds image"]
    Build --> Push["Image pushed to a registry"]
    Push --> Orc["Orchestrator schedules containers across a cluster"]
    Orc --> N1["Node 1: containerd + runc"]
    Orc --> N2["Node 2: containerd + runc"]
    Orc --> N3["Node 3: containerd + runc"]
Figure 4: From a Dockerfile to containers spread across a cluster

Every major cloud provider offers managed container services built on these same open standards. Amazon Web Services offers Amazon ECS, its own orchestrator, and Amazon EKS, a managed version of Kubernetes, alongside AWS Fargate, which runs containers without the customer having to manage any servers at all. Google Cloud and Microsoft Azure offer comparable managed Kubernetes services of their own. In every case, the image you built with the Docker Engine on your laptop is the same image these cloud services run, because they all speak the same open container format.

Continuous delivery in practice

A typical modern pipeline works like this: a developer pushes code, an automated build server builds a new image and tags it with a version number, a vulnerability scanner checks it, the image is pushed to a registry, and the orchestrator gradually replaces old containers with new ones, often a few at a time, so that if something is wrong, only a small slice of traffic is affected before the rollout is stopped automatically.

GitOps: letting the cluster read its own instructions

Many teams take this one step further with an approach called GitOps. Instead of an engineer running deployment commands by hand, the desired state of the whole system, which images should be running and how many copies of each, is written down as plain configuration files stored in a version-controlled repository, the same kind of system used to store source code. A small automated controller inside the cluster continuously compares that stored description with what is actually running, and quietly corrects any difference it finds. This means every change to production has a written history, can be reviewed before it happens, and can be undone simply by reverting the configuration file to an earlier version.

14Design Patterns and Anti-patterns

Certain shapes of using containers work well again and again. Others cause pain that looks different every time but has the same root cause.

Pattern

One process per container

Each container runs a single main responsibility, such as the web server or the database, making it simple to scale, replace or restart independently.

Pattern

Sidecar

A helper container runs alongside the main one to add a capability such as log shipping or traffic encryption, without changing the main application’s code.

Pattern

Multi-stage build

A Dockerfile uses one stage with heavy compiler tools to build the app, then copies only the finished result into a small, clean final image, keeping it lean.

Pattern

Immutable image

Once built and tagged, an image is never changed in place. A fix always means building and deploying a brand-new image.

ANTI-PATTERN · THE MONSTER CONTAINERAVOID
What it looks like

A single container running a web server, a database, a scheduled job and a message queue all at once, because it felt simpler to set up that way at first.

Why it hurts later

The whole bundle must be restarted together even if only one piece needs a restart. It cannot be scaled piece by piece, since scaling the container scales everything inside it equally. And a crash in one piece can bring down the log output and health checks for all the others, making the real cause hard to find.

The fix

Split each responsibility into its own container, connect them with a network, and let an orchestrator manage and scale each one on its own.

ANTI-PATTERN · STORING SECRETS IN THE IMAGEAVOID
What it looks like

Baking a database password or an API key directly into a Dockerfile instruction or into the application’s source code before building the image.

Why it hurts later

Every layer of an image is easy to inspect, and the image is often pushed to a shared registry. Anyone who can pull the image can typically recover that secret, even from an old layer that a later instruction appeared to remove.

The fix

Pass secrets in at container start-up time, through a secrets manager or an orchestrator’s built-in secrets feature, so they never become part of the image itself.

15Best Practices and Common Mistakes

A short, practical checklist that catches most of the problems beginners run into.

Use small base images

Choosing a minimal base such as Alpine or a “slim” variant shrinks build time, download time and the space for vulnerabilities to hide.

Order Dockerfile steps well

Put rarely changing steps first and frequently changing steps, like copying source code, last, so the build cache does more work for you.

Pin versions

Referencing an exact image version or digest, rather than a moving tag like “latest,” avoids surprise changes when a base image updates.

Set resource limits

Giving every container a memory and CPU limit prevents one runaway process from starving its neighbours on the same host.

Clean up unused data

Old images, stopped containers and unused volumes quietly consume disk space; regular cleanup keeps hosts healthy.

Write a .dockerignore file

Excludes files such as local development folders and secrets from ever being sent to the build process in the first place.

!
Common mistake: ignoring graceful shutdown

When the engine stops a container, it sends a polite termination signal first and only forcibly kills the process after a short grace period. An application that ignores this signal loses in-flight requests and can corrupt data on every deployment. Well-behaved applications listen for this signal and shut down cleanly within the grace period.

16Real-World and Industry Examples

These ideas are not theoretical. Some of the largest technology companies in the world run on containers every single day.

Streaming

Netflix

Runs a large share of its backend services in containers scheduled across its own infrastructure, letting it deploy thousands of small services independently and scale them separately during peak viewing hours.

Cloud provider

Amazon

Built Amazon ECS and Amazon EKS specifically to run customer containers at massive scale, and created Firecracker, the lightweight virtualisation technology that also powers AWS Lambda and Fargate.

Ride-hailing

Uber

Moved from a single large application to hundreds of independently deployable services, many running in containers, to let separate teams ship updates without waiting on each other.

Search and cloud

Google

Runs essentially all of its internal services in containers and originally built the ideas behind Kubernetes from over a decade of running containers internally at enormous scale.

A common thread across all of these companies is the same one described at the start of this guide: containers let a huge number of independent teams package their own piece of software once and trust that it will run the same way everywhere it is deployed, from a laptop to a global fleet of servers.

17Frequently Asked Questions

Q1Is Docker the same thing as a container?
No. A container is a general concept, made possible by Linux kernel features that existed before Docker. Docker is a specific set of tools, including the engine described in this guide, that made creating and using containers dramatically easier for everyday developers.
Q2Do I need Docker Desktop to use the Docker Engine?
On Linux, no — the engine can be installed and run directly, since Linux already has the kernel features containers need. On Mac and Windows, Docker Desktop is the common way to get the engine running, because it also provides the small Linux virtual machine that Linux containers require on those operating systems.
Q3Why did Kubernetes stop using Docker directly?
Kubernetes needed a simpler, more direct interface to container runtimes than talking to the full Docker Engine provided. It switched to talking to containerd and similar runtimes directly through a standard interface. Because both Docker and Kubernetes rely on the same open container standards underneath, images built with Docker continue to run on Kubernetes without any change.
Q4Is running containers as root always dangerous?
Root inside a container is not identical to root on the host, since namespaces and reduced capabilities limit what it can do. Still, it is safer practice to run the application process as a non-root user inside the container whenever the application supports it, adding one more layer of defence.
Q5What happens to my data if I delete a container?
Anything written only inside the container’s own writable layer is deleted along with the container. Anything written to an attached volume survives, because a volume is stored and managed separately from any single container.
Q6Can containers from different images talk to each other?
Yes, as long as they are attached to the same Docker network. They can reach each other by container name on a user-created network, thanks to the engine’s built-in internal DNS.
Q7Do I need Kubernetes to use Docker in production?
Not necessarily. A single server or a small number of servers running the Docker Engine directly, perhaps with its built-in swarm mode, is a reasonable choice for smaller workloads. Kubernetes becomes valuable once you have many services, many servers, and a need for automatic scaling and self-healing at scale.
Q8Why do people say containers are “immutable”?
Immutable means unchangeable. Once an image is built and tagged, best practice treats its contents as fixed and never edits it in place on a running server. If a bug is found or a setting must change, the correct fix is to build a brand-new image with the correction and deploy that, rather than logging into a running container and changing files by hand, which would create a version of the software that exists nowhere else and cannot be reproduced later.
Q9Is Docker Engine free to use?
Yes. The Docker Engine itself is open-source software, released under a permissive license, and can be installed and used on Linux servers without charge. Docker Inc. instead charges for Docker Desktop subscriptions above certain company sizes, and for additional paid services such as hosted build infrastructure, while the underlying engine and the open container standards it follows remain freely available.

18Summary and Key Takeaways

Carry these ideas forward

  • The Docker Engine is a client-server system: a lightweight CLI you type into, an API in between, and a daemon that does the real work through containerd and runc.
  • Images are frozen blueprints; containers are the running copies made from them. Deleting a container never touches the image it came from.
  • Isolation is built from ordinary Linux features — namespaces for a private view of the system, and control groups for fair sharing of resources — not from a separate pretend computer like a virtual machine.
  • Layers make images fast to build and download by reusing anything unchanged, and copy-on-write keeps a running container’s changes separate from its shared, read-only image.
  • Data written inside a container disappears when it is removed unless it lives in a volume, so plan storage deliberately from the start.
  • Security is a stack of defences, not one wall: non-root users, seccomp, capability limits and, for the highest-risk workloads, an even stronger boundary such as a lightweight VM.
  • At scale, an orchestrator such as Kubernetes or Docker’s own swarm mode decides where containers run, restarts failures, and spreads traffic — and every major cloud provider builds managed services on the same open container standards.