Install & Verify Docker
Output on this page comes from Docker Desktop 4.81.0 (Engine 29.6.1) on macOS, Apple Silicon, 2026-09-30. The Linux install steps are copied from the official docs and were not run here (this Mac has no apt); what was checked is noted where it matters.
Installation options
Section titled “Installation options”Docker ships as two main packages, depending on your operating system:
- Docker Desktop — a GUI application that bundles the Docker Engine, Docker Compose, Buildx, an optional single-node Kubernetes cluster, and a dashboard, for macOS, Windows and Linux. The engine runs inside a lightweight VM.
- Docker Engine — the daemon and CLI for Linux, installed from packages. No GUI, lighter, and the usual choice for servers and CI. The engine runs directly on the host kernel.
Download Docker Desktop from docs.docker.com/desktop/setup/install/mac-install (the older /desktop/install/mac-install/ URL redirects there), pick your chip (Apple Silicon or Intel), then drag Docker.app into Applications and launch it.
Docker is ready once the whale icon in the menu bar shows “Docker Desktop is running.”
Windows
Section titled “Windows”Download the Docker Desktop installer from docs.docker.com/desktop/setup/install/windows-install. Docker Desktop on Windows can use WSL 2 (Windows Subsystem for Linux) as its backend, which the installer helps configure if it is not already enabled.
Restart when prompted; Docker Desktop then runs from the system tray.
On Linux you install Docker Engine directly from Docker’s own apt repository. The steps below follow Install Docker Engine on Ubuntu and on Debian as of 2026-09-30. The repository URL is distribution-specific: download.docker.com/linux/ubuntu for Ubuntu and download.docker.com/linux/debian for Debian. Using the Ubuntu path on Debian points apt at packages built for a different distribution.
#!/bin/shset -eu# Set DISTRO to ubuntu or debianDISTRO=ubuntu
# Add Docker's official GPG keysudo apt-get updatesudo apt-get install -y ca-certificates curlsudo install -m 0755 -d /etc/apt/keyringssudo curl -fsSL "https://download.docker.com/linux/$DISTRO/gpg" -o /etc/apt/keyrings/docker.ascsudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to apt sources (deb822 format used by the current docs)# Ubuntu derivatives report UBUNTU_CODENAME; Debian only has VERSION_CODENAMEsudo tee /etc/apt/sources.list.d/docker.sources <<EOFTypes: debURIs: https://download.docker.com/linux/$DISTROSuites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")Components: stableArchitectures: $(dpkg --print-architecture)Signed-By: /etc/apt/keyrings/docker.ascEOFsudo apt-get update
# Install Docker Engine, CLI, containerd, Buildx and Compose pluginssudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginWhat was checked locally: the script passes bash -n, and on 2026-09-30 both https://download.docker.com/linux/ubuntu/gpg and https://download.docker.com/linux/debian/gpg (and their dists/ directories) answer HTTP 200. Running it needs an Ubuntu or Debian host with sudo. For other distributions (Fedora, RHEL, Arch) the package names and package manager differ, but the pattern is the same: add Docker’s repository, then install the engine, CLI, and containerd packages — see docs.docker.com/engine/install.
On Debian, testing and derivatives such as Kali may need the codename replaced by the matching Debian release name (the docs use trixie as the example).
After installation, start the daemon and optionally enable it on boot (the DEB packages usually start it for you):
sudo systemctl start dockersudo systemctl enable dockerTo run Docker commands without sudo, add your user to the docker group:
sudo usermod -aG docker $USERnewgrp dockernewgrp docker applies the group change to your current shell right away; logging out and back in works too.
Verifying the installation
Section titled “Verifying the installation”docker version
Section titled “docker version”docker versionReal output from this Mac (Docker Desktop):
Client: Version: 29.6.1 API version: 1.55 Go version: go1.26.4 Git commit: 8900f1d Built: Fri Jun 26 11:39:35 2026 OS/Arch: darwin/arm64 Context: desktop-linux
Server: Docker Desktop 4.81.0 (232925) Engine: Version: 29.6.1 API version: 1.55 (minimum version 1.40) Go version: go1.26.4 Git commit: 8ec5ab3 Built: Fri Jun 26 11:39:58 2026 OS/Arch: linux/arm64 Experimental: false containerd: Version: v2.2.5 GitCommit: e53c7c1516c3b2bff98eb76f1f4117477e6f4e66 runc: Version: 1.3.6 GitCommit: v1.3.6-0-g491b69ba docker-init: Version: 0.19.0 GitCommit: de40ad0Both a Client section and a Server section must appear. If the Server section is missing, the daemon is not running. On a Linux Engine install the first line of the server block is Server: Docker Engine - Community, OS/Arch is linux/amd64 or linux/arm64 on both sides, and Context is default. On this Mac the client is darwin/arm64 and the server linux/arm64: the CLI is native, the engine is in the VM.
When the daemon is unreachable the Client block still prints, then the command fails (exit code 1):
$ DOCKER_HOST=unix:///nonexistent.sock docker version | tail -2failed to connect to the docker API at unix:///nonexistent.sock; check if the path is correct and if the daemon is running: dial unix /nonexistent.sock: connect: no such file or directorydocker info
Section titled “docker info”docker infoThis prints the daemon’s full configuration. The fields you check first (real values from this Mac):
Server Version: 29.6.1Storage Driver: overlay2Logging Driver: json-fileCgroup Driver: cgroupfsCgroup Version: 2Plugins: Network: bridge host ipvlan macvlan null overlayRuntimes: io.containerd.runc.v2 runcKernel Version: 6.12.76-linuxkitOperating System: Docker DesktopArchitecture: aarch64CPUs: 11Total Memory: 8.721GiBDocker Root Dir: /var/lib/dockerOperating System: Docker Desktop and Kernel Version: …-linuxkit are the tell that you are on the Desktop VM; on a Linux server they would show your distribution and kernel. Total Memory is what the VM was given, not your Mac’s RAM. docker info also lists the six network drivers (bridge host ipvlan macvlan null overlay), which the networking module uses.
docker run hello-world
Section titled “docker run hello-world”docker run hello-worldReal output on a machine that has never pulled the image (docker rmi hello-world first):
Unable to find image 'hello-world:latest' locallylatest: Pulling from library/hello-world58dee6a49ef1: Pulling fs layer58dee6a49ef1: Verifying Checksum58dee6a49ef1: Download complete58dee6a49ef1: Pull completeDigest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8Status: Downloaded newer image for hello-world:latest
Hello from Docker!This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm64v8) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.The (arm64v8) note is the platform picked from the image’s multi-platform index. The layer ID and Digest will differ over time; the shape will not. Seeing “Hello from Docker!” confirms the full pipeline is working: CLI, daemon, image pull from Docker Hub, and container execution.
docker version
docker run hello-worldReading AI-generated code
Section titled “Reading AI-generated code”An AI-written provisioning snippet for a shared build server:
🐛 Spot the bug
#!/bin/sh# AI: "let the deploy user run docker without sudo.# This is just a convenience: it does not grant any extra privileges."sudo usermod -aG docker deployShow the answer
Membership of the docker group is a privilege: anyone who can reach the socket can ask the engine to mount any path of the engine host into a container and read it as root. Proven on this Mac by mounting the engine host’s root read-only from a throw-away container with no network:
$ docker run --rm --network none -v /:/host:ro alpine:3.24 sh -c 'head -c 60 /host/etc/os-release; ls /host | head -5'PRETTY_NAME="Docker Desktop"UsersVolumesbinboot...Two Docker Desktop specifics: the / here is the VM’s root (not macOS’s), and Desktop’s file sharing maps files under your home directory back to your own macOS user, so this demo does not hand out macOS root. On a Linux Engine the daemon runs as root on the real host, so the same mount exposes real host files such as /etc/shadow (not run here — this Mac has no Linux Engine); that is what the docs mean by “root-level privileges”. A least-privilege alternative is rootless mode, where the daemon itself runs as your user. Per the rootless docs:
#!/bin/sh# Run as the non-root user that should own the daemon (docs.docker.com/engine/security/rootless/)dockerd-rootless-setuptool.sh installexport DOCKER_HOST="unix:///run/user/$(id -u)/docker.sock"Rootless has its own limits (for example, low ports and some network drivers); it lowers the blast radius rather than removing the trade-off.
Mental Model
Section titled “Mental Model”Installing Docker means installing two things: a client that any user can run, and an engine that runs with high privilege (as root on Linux, inside a VM on Desktop). Verification therefore has two halves — can the client run, and can it reach a working engine that can pull and start something. docker version proves the first, the Server block and hello-world prove the second.
Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Docker Desktop | Easy to install, GUI, bundles Compose, Buildx and optional Kubernetes | Uses more machine resources (a VM), and requires a paid subscription for larger organizations (Docker subscription terms) |
| Docker Engine | Lightweight, well suited to servers and CI, native networking and paths | No GUI — everything is managed through the CLI; you own upgrades |
docker group access | Commands without sudo | Root-equivalent access to the host for every member |
| Rootless mode | Daemon and containers run without root | Feature limits (ports, some network drivers), more setup |
Common mistakes
Section titled “Common mistakes”- Running
docker runwithout first checking that the daemon is up (a missing Server section indocker version), then hitting confusing errors. - Installing Docker Engine on Linux and forgetting to add your user to the
dockergroup, so every command needssudoor fails with a permission error on/var/run/docker.sock— then adding the whole team to the group without realising it is root. - Assuming Docker Desktop is required on every OS, when Linux should use Docker Engine directly because it is lighter.
- Pasting the Ubuntu repository URL on a Debian host (or the reverse).
Common Misconceptions
Section titled “Common Misconceptions”- “
docker versionsucceeded, so Docker works.” The Client block prints even with no daemon; check that the Server block is present, or runhello-world. - “The docker group is like the
staffgroup.” It is closer tosudowithout a password prompt. - “Docker Desktop and Docker Engine are the same thing.” Desktop is a product that bundles an engine in a VM plus tooling; Engine is the daemon package you install on Linux.