Skip to content

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.

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.”

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.

scripts/install-docker-apt.sh
#!/bin/sh
set -eu
# Set DISTRO to ubuntu or debian
DISTRO=ubuntu
# Add Docker's official GPG key
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL "https://download.docker.com/linux/$DISTRO/gpg" -o /etc/apt/keyrings/docker.asc
sudo 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_CODENAME
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/$DISTRO
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt-get update
# Install Docker Engine, CLI, containerd, Buildx and Compose plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

What 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):

Terminal window
sudo systemctl start docker
sudo systemctl enable docker

To run Docker commands without sudo, add your user to the docker group:

Terminal window
sudo usermod -aG docker $USER
newgrp docker

newgrp docker applies the group change to your current shell right away; logging out and back in works too.

Terminal window
docker version

Real 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: de40ad0

Both 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 -2
failed 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 directory
Terminal window
docker info

This prints the daemon’s full configuration. The fields you check first (real values from this Mac):

Server Version: 29.6.1
Storage Driver: overlay2
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 2
Plugins:
Network: bridge host ipvlan macvlan null overlay
Runtimes: io.containerd.runc.v2 runc
Kernel Version: 6.12.76-linuxkit
Operating System: Docker Desktop
Architecture: aarch64
CPUs: 11
Total Memory: 8.721GiB
Docker Root Dir: /var/lib/docker

Operating 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.

Terminal window
docker run hello-world

Real output on a machine that has never pulled the image (docker rmi hello-world first):

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
58dee6a49ef1: Pulling fs layer
58dee6a49ef1: Verifying Checksum
58dee6a49ef1: Download complete
58dee6a49ef1: Pull complete
Digest: sha256:5e23090353324d887c48ad5e5c56d294eab81588df9605b07d1afe895f9cc8f8
Status: 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-world

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 deploy
Show 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"
Users
Volumes
bin
boot
...

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:

scripts/rootless.sh
#!/bin/sh
# Run as the non-root user that should own the daemon (docs.docker.com/engine/security/rootless/)
dockerd-rootless-setuptool.sh install
export 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.

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.

OptionBenefitCost
Docker DesktopEasy to install, GUI, bundles Compose, Buildx and optional KubernetesUses more machine resources (a VM), and requires a paid subscription for larger organizations (Docker subscription terms)
Docker EngineLightweight, well suited to servers and CI, native networking and pathsNo GUI — everything is managed through the CLI; you own upgrades
docker group accessCommands without sudoRoot-equivalent access to the host for every member
Rootless modeDaemon and containers run without rootFeature limits (ports, some network drivers), more setup
  • Running docker run without first checking that the daemon is up (a missing Server section in docker version), then hitting confusing errors.
  • Installing Docker Engine on Linux and forgetting to add your user to the docker group, so every command needs sudo or 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).
  • “docker version succeeded, so Docker works.” The Client block prints even with no daemon; check that the Server block is present, or run hello-world.
  • “The docker group is like the staff group.” It is closer to sudo without 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.
Which output of docker version tells you the daemon is running and reachable?
You install Docker Engine on Debian using the apt repository. Which repository path is correct?
What does adding a user to the docker group effectively grant?
On a Mac, docker info shows Operating System: Docker Desktop and Kernel Version: 6.12.76-linuxkit. What does that mean?
A setup script checks docker --version and then prints 'Docker is ready'. Which command actually proves the engine works end to end?