Getting Started with Docker

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages.

Docker packages an application together with everything it needs to run — its runtime, libraries, configuration defaults and start command — into an image, and runs that image as an isolated process called a container on any machine that has a container engine. This page is the entry point to the section: it explains the problem Docker solves, what it deliberately does not do, how the pieces of today’s Docker platform fit together, how to install it, and which first commands to run. Every page in this section is written against Docker Engine 29.8.x, Docker Compose v5.5.x, Buildx 0.37.x with BuildKit 0.33.x and the docker/dockerfile:1 frontend, and Docker Desktop 4.91 or later.

The problem Docker solves

Before containers, "deploying an application" meant reproducing a working environment on another machine: the right language runtime, the right system libraries, the right configuration files, and a start script that assumed all of the above. Every difference between a developer’s laptop, the CI server and production was a potential failure, and the deployment artifact (a JAR, a tarball, a package) carried none of that environment with it.

Docker changes the unit of deployment. The artifact becomes an image that already contains the environment, and the only contract with the host is "run this image with a container engine". The same image is built once, tested in CI, pushed to a registry and pulled unchanged into every environment. This gives four practical properties:

  • Reproducibility — the image is immutable and addressed by a content digest, so what was tested is exactly what runs.

  • Isolation — each container sees its own filesystem, process tree, network stack and resource limits, so two applications with conflicting dependencies can share one host.

  • Density and speed — containers are ordinary processes on the host kernel; there is no guest operating system to boot, so they start in milliseconds and share memory pages with other containers.

  • A standard workflow — build, ship, run: docker build, docker push, docker run work the same way for a Java service, a database or a command-line tool.

What Docker is not

The first chapter of Docker: Up & Running lists what Docker is not, and the list still holds against the current Docker overview:

  • Not a virtual machine. A container shares the host’s kernel. It cannot run a different kernel, and a Linux container needs a Linux kernel — which is why Docker Desktop runs one inside a lightweight VM on macOS and Windows (see Docker Architecture and the OCI).

  • Not a configuration-management tool. Docker does not converge long-lived servers towards a desired state the way Ansible, Puppet or Chef do. A container is replaced, not patched: change the image, start a new container.

  • Not an orchestrator. A single Docker Engine runs containers on one host. Scheduling across many hosts, self-healing and rolling updates belong to an orchestrator — Kubernetes, or Docker’s own Swarm mode (see Orchestration: Swarm Mode and Kubernetes).

  • Not a security boundary as strong as a VM. Namespaces and cgroups isolate well, but the kernel is shared; see Docker Security for what that implies.

Containers vs. virtual machines

A virtual machine virtualises hardware: a hypervisor presents virtual CPUs, memory and devices, and each VM boots a complete guest operating system. A container virtualises the operating system: the host kernel gives each container its own view of the process table, mounts, network interfaces, hostname and users (namespaces), and limits what it may consume (cgroups).

flowchart TB subgraph VM["Virtual machines"] direction TB va["App A + libraries"] --> vga["Guest OS kernel A"] vb["App B + libraries"] --> vgb["Guest OS kernel B"] vga --> hv["Hypervisor"] vgb --> hv hv --> hw1["Host hardware"] end subgraph CT["Containers"] direction TB ca["Container A: app + libraries"] --> eng["Container engine: dockerd, containerd, runc"] cb["Container B: app + libraries"] --> eng eng --> hk["Shared host kernel: namespaces and cgroups"] hk --> hw2["Host hardware"] end
Virtual machine Container

What is virtualised

Hardware (CPU, memory, devices)

The operating system’s view (processes, mounts, network, users)

Kernel

One per VM, any operating system

The host’s kernel, shared by every container

Start-up time

Seconds to minutes (boot a guest OS)

Milliseconds to seconds (start a process)

Size of the artifact

Gigabytes (a disk image with a full OS)

Megabytes to hundreds of megabytes (only the userland the app needs)

Isolation strength

Strong: a hypervisor boundary

Good, but the kernel is shared; hardened further with user namespaces, seccomp and rootless mode

Typical use

Running different operating systems, strong multi-tenant isolation

Packaging and running applications, CI, local development environments

The two combine rather than compete: cloud providers run containers inside VMs, and Docker Desktop runs the Docker Engine inside a VM. Official page: Docker overview.

The platform map

"Docker" names a company, a set of products and an open-source engine. The pieces a backend developer meets are:

Docker platform map showing the developer laptop with Desktop and the CLI and Compose and Testcontainers; CI with Buildx; registries such as Docker Hub; and runtime hosts running Docker Engine or Kubernetes
Component What it is

Docker Engine

The daemon (dockerd) plus containerd and runc that build, store and run containers on a Linux host. Open source (the Moby project). See Docker Architecture and the OCI.

Docker CLI

The docker command. A client that talks to an Engine through its REST API, locally or remotely.

Docker Desktop

The desktop application for macOS, Windows and Linux: a managed Linux VM running the Engine, plus the CLI, Compose, Buildx, a Kubernetes cluster, a dashboard and file sharing with the host.

Docker Hub

The default public registry: Docker Official Images, Verified Publisher images and your own repositories. See Registries and Docker Hub.

Docker Build (Buildx and BuildKit)

The build system: BuildKit is the engine, docker buildx the client. See BuildKit and Buildx.

Docker Compose

Defines and runs multi-container applications from a compose.yaml. See Docker Compose Fundamentals.

Docker Scout

Image analysis: SBOMs, CVEs, base-image recommendations and policies. See Image Best Practices and Supply-Chain Security.

Docker Hardened Images

Minimal, continuously patched base images with provenance and SBOMs.

Testcontainers

Libraries that start throwaway containers from test code. See Testcontainers Fundamentals.

Docker also ships products this section only names: Docker Sandboxes, Docker Model Runner, the MCP Catalog and Toolkit, Docker Offload and Docker Build Cloud. They are documented under Docker AI, Docker Offload and Docker Build Cloud and are out of scope here.

Installing Docker

There are two supported ways to get a Docker Engine on a developer machine:

Docker Desktop Docker Engine (Linux only)

Platforms

macOS (Apple silicon and Intel), Windows (WSL 2 or Hyper-V), Linux

Linux distributions: Ubuntu, Debian, Fedora, RHEL, CentOS Stream, SLES, Raspberry Pi OS and static binaries

What you get

Engine in a managed VM, CLI, Compose, Buildx, Kubernetes, Scout, a GUI, automatic updates

The daemon and CLI packages (docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, docker-compose-plugin)

Licensing

Free for personal use, education, open source and small businesses; a paid subscription for larger companies

Open source (Apache 2.0)

Typical use

Developer laptops

Linux workstations, servers, CI runners

On Linux, install Docker Engine from Docker’s own apt/dnf repository rather than the distribution’s package, as described in Install Docker Engine. The convenience script is fine for a throwaway VM but not for a server you care about:

# Throwaway test machines only -- review the script before piping it to a shell
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

On macOS and Windows install Docker Desktop from Get Docker; it brings the CLI, Compose and Buildx with it.

Post-install steps on Linux

By default the Docker socket (/var/run/docker.sock) is owned by root, so every docker command needs sudo. The documented shortcut is to add your user to the docker group:

sudo groupadd docker          # usually already created by the package
sudo usermod -aG docker $USER
newgrp docker                 # or log out and back in
docker run hello-world        # now works without sudo

Membership of the docker group is root-equivalent: anyone who can talk to the daemon can start a container that bind-mounts / from the host and runs as root inside it. Treat the group like sudo access. If that is not acceptable, run the daemon in rootless mode instead (see Docker Security).

The same Linux post-installation steps page also shows how to start the daemon on boot with systemd:

sudo systemctl enable docker.service containerd.service

First commands

docker version        # client and server versions, API version, containerd and runc versions
docker info           # storage driver, cgroup version, number of containers/images, registry mirrors, warnings
docker run hello-world

docker version is the fastest way to confirm the CLI can reach a daemon: when the Server: section is missing, the daemon is not running or the CLI is pointed at the wrong socket. docker run hello-world exercises the whole path end to end — the CLI asks the daemon to run hello-world, the daemon pulls the image from Docker Hub because it is not present locally, creates a container, starts it, streams its output back and the container exits. Derived from Docker overview and docker version.

A slightly more useful first container runs a web server in the background and publishes its port:

docker run -d --name web -p 8080:80 nginx:1.29
curl -s http://localhost:8080 | head -n 4
docker logs web
docker rm -f web

Derived from Publishing and exposing ports. Running Containers explains every flag.

Docker contexts

The CLI does not have to talk to a local daemon. A context bundles the endpoint (a Unix socket, tcp:// with TLS, or ssh://) and its credentials under a name, and docker context use switches every subsequent command to it:

docker context ls                                        # * marks the current context
docker context create build-box --docker "host=ssh://ci@build-box.internal"
docker context use build-box
docker ps                                                # now lists containers on build-box
docker --context default ps                              # one-off override
docker context use default

Derived from Docker contexts. Docker Desktop creates its own desktop-linux context. The DOCKER_HOST and DOCKER_CONTEXT environment variables override the selected context. Tools that talk to the API directly do not necessarily read contexts: Testcontainers, for example, looks for DOCKER_HOST, its own tc.host setting and well-known socket paths instead (see Testcontainers Fundamentals).

Version history behind the numbers in older books

The two 2015 books listed in the bibliography describe Docker 1.3 to 1.6. The numbering and the platform have changed several times since:

Version Year What changed

1.0 to 1.13

2014 to 2017

The original monolithic daemon. Execution drivers (LXC, then libcontainer), since replaced by containerd and runc (see Docker Architecture and the OCI); AUFS and devicemapper storage, since replaced by overlay2 and the containerd image store; container links (--link), since replaced by user-defined networks with embedded DNS (see Networking); and docker-compose v1 (Python, formerly Fig), since replaced by the docker compose plugin, Compose v2/v5 (see Docker Compose Fundamentals). Boot2Docker and Docker Machine ran the daemon in a VirtualBox VM on macOS and Windows.

17.03

2017

Calendar versioning (YY.MM) and the split into Community Edition (CE) and Enterprise Edition (EE). The engine is developed in the open as the Moby project; containerd and runc are separate components.

17.05

2017

Multi-stage builds in the Dockerfile.

18.09 to 20.10

2018 to 2020

BuildKit available as an opt-in builder; cgroup v2 support (20.10); rootless mode graduates. Compose v2, announced in 2020 and rewritten in Go, ships as the docker compose CLI plugin and became generally available in April 2022, while 20.10 was still the current engine; docker-compose v1 then reached end of life.

23.0

2023

Calendar versioning is dropped for SemVer-style numbering, so later releases have integer major versions (24.0, 25.0, …​). Buildx and BuildKit become the default builder on Linux; the legacy builder is deprecated.

25.0 to 28

2024 to 2025

The containerd image store as an option.

29

2025

The containerd image store is the default for fresh installs, Docker Content Trust is removed from the CLI, cgroup v1 is deprecated, nftables can be selected as the firewall backend, legacy-link environment variables are no longer injected, and docker image ls shows the new tree-style view.

Derived from the Docker Engine release notes, Engine 23.0 release notes (the switch to SemVer), Engine 29 release notes and History and development of Docker Compose. Boot2Docker and Docker Machine are historical: Docker Desktop replaced them on macOS and Windows, and on Linux you install Docker Engine directly. Vagrant-based setups from the same era are likewise unnecessary.

Where to go next

Read Docker Architecture and the OCI to see what happens behind docker run, then Images, Layers and Tags and Dockerfile Fundamentals. The full reading order is on the section landing page.