A plain-English guide to core Docker concepts: images, containers, Dockerfile, registry, volumes, networks, Compose, and how the client talks to the daemon.
Docker has perhaps eight ideas in it. Most early confusion comes from three of them sounding interchangeable when they are not, image, container and Dockerfile, so this page defines each precisely and shows how they connect.
A Dockerfile is a recipe. Building it produces an image, which is a read-only template. Running an image produces a container, which is a live process. A registry is where images are stored and shared.
Everything else, volumes, networks, Compose, exists to make containers useful together.
Dockerfile ──docker build──▶ Image ──docker run──▶ Container
│ ▲
docker push│ │docker pull
▼ │
RegistryA read-only template containing everything needed to run an application: the code, the language runtime, system libraries, and the metadata describing how to start it.
An image is built from layers, one per instruction in the Dockerfile, stacked and presented as a single filesystem. Layers are what make Docker efficient: they are content-addressed and shared, so ten images built on node:22-alpine store that base exactly once on disk and pull it exactly once from a registry.
Two consequences of the layer model worth internalising early. Layers are immutable, so a file added in one layer and deleted in a later one still occupies space in the image, and is still recoverable, which is why a deleted secret is a leaked secret. And layers are cached, so the order of instructions in a Dockerfile largely determines how fast rebuilds are.
Go deeper: Docker images and image layers.
docker imagesA running instance of an image. If the image is the recipe, the container is the meal.
Technically it is an ordinary Linux process with restricted visibility. Namespaces give it its own view of the filesystem, process table, network stack and hostname; cgroups cap what CPU and memory it can consume. There is no virtual machine and no guest operating system. This is why a container starts in milliseconds where a VM takes a minute.
Docker adds one thin writable layer on top of the image's read-only layers. Every file the container creates or modifies lands there, and it is deleted with the container. That single fact explains most Docker surprises for newcomers: your database is empty again because its data lived in the writable layer of a container that no longer exists.
Containers are ephemeral by design. Treat them as disposable and keep anything that must survive in a volume.
Go deeper: Docker containers and the container lifecycle.
docker psA plain text file of instructions for building an image, executed top to bottom:
Each instruction produces a layer. The reason the package manifest is copied and installed before the source is that Docker reuses cached layers up to the first changed instruction, so copying source first would reinstall every dependency on every code edit.
Go deeper: Dockerfile basics.
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]Storage and distribution for images. GitHub, but for images rather than source. You push to share and pull to consume.
Docker Hub is the default: if you run docker pull nginx with no registry prefix, that is where it comes from. Most organisations also use a private registry. GitHub Container Registry, Amazon ECR, Google Artifact Registry, Azure Container Registry. For their own images.
A tag such as 1.0 or latest is a mutable pointer to an image, not a permanent name. latest in particular is just a default tag with no special meaning; it does not track the newest version. A digest (@sha256:…) is immutable and is what you pin when reproducibility matters.
Go deeper: Docker Hub and registries.
docker pull postgres:17
docker tag myapp:1.0 ghcr.io/acme/myapp:1.0
docker push ghcr.io/acme/myapp:1.0Storage that outlives the container. Since the container's writable layer is destroyed with it, anything that must persist, database files, uploads, goes in a volume.
Three kinds, and the distinction comes up constantly:
Go deeper: Docker volumes.
docker volume create pgdata
docker run -d -v pgdata:/var/lib/postgresql/data postgres:17Containers on the same user-defined network reach each other by container name, which is Docker's built-in DNS at work:
The API connects to db:5432. No IP addresses, no --link, nothing to update when a container restarts with a different address.
Note that localhost inside a container means that container, not the host and not another container. Expecting localhost:5432 to reach a database in a different container is one of the most common beginner errors.
Go deeper: container networking.
docker network create appnet
docker run -d --name db --network appnet postgres:17
docker run -d --name api --network appnet myapp:1.0Real applications are several containers. Compose describes them in one file so the whole stack starts together:
Compose creates a network automatically, which is why db resolves as a hostname without any extra configuration.
It is a single-host tool. Excellent for development and small deployments, not a replacement for an orchestrator. Once you need scheduling across machines, rolling updates and self-healing, that is Kubernetes.
services:
api:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on:
db:
condition: service_healthy
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
volumes:
pgdata:One more piece, because it explains a whole class of error messages.
The docker command is a client. It does almost nothing itself. It sends requests over a socket to the daemon (dockerd), which builds images, runs containers and manages storage.
So Cannot connect to the Docker daemon means the CLI is installed and working, and nothing is listening. Start the service. And permission denied ... docker.sock means the daemon is running but your user cannot talk to its socket.
It also explains why the build context is sent to the daemon: the daemon may not be on your machine at all.
docker CLI ──/var/run/docker.sock──▶ dockerd ──▶ containerd ──▶ runc ──▶ your processA Postgres container is started with docker run, data is written, then the container is removed and recreated from the same image. The data is gone. Which concept explains this?
Learn what a Docker image is, how layers and tags work, how to shrink images, and how to reclaim disk space by removing dangling images and old tags.
Understand how Docker image names and tags are structured, tag images with docker tag, avoid the latest trap, and pin exact versions using digests.
Learn what Docker Hub is, how to find official images, and how to work around pull rate limits. A practical intro to using the default Docker registry.