Installing Docker
Installing Docker gives your machine the tools needed to pull images, build images, and run containers. The important thing to understand is that the docker command is only the client; containers actually run through a Docker daemon, either on your Linux host or inside the lightweight Linux VM managed by Docker Desktop.
For most learners on macOS or Windows, Docker Desktop is the easiest install. On Linux servers, the usual choice is Docker Engine from Docker’s official package repository.
Overview: How Installation Works
A working Docker installation has several parts. The Docker CLI is the command you type, such as docker run or docker build. The Docker daemon, often called dockerd, listens for API requests from the CLI and does the real work: pulling image layers, creating container filesystems, wiring networks, attaching volumes, and starting processes. Docker also installs containerd and runc, lower-level components that help manage containers according to Open Container Initiative standards.
On native Linux, Docker Engine uses Linux kernel features directly. Namespaces isolate process IDs, networking, mounts, users, and hostnames. Control groups limit and account for CPU and memory. A union filesystem stacks read-only image layers and adds a thin writable layer for each container. This is why installing Docker Engine normally requires root privileges and a running system service.
On macOS and most Windows development machines, Docker cannot use the host kernel directly for Linux containers because the host kernel is not Linux. Docker Desktop solves that by running a small Linux VM. Your terminal still runs docker, but the CLI talks to a daemon inside that VM. Images, containers, named volumes, and Linux filesystems live inside Docker Desktop’s managed environment. Bind mounts connect selected host directories into the VM so containers can see your project files.
There are two main installation tracks. Docker Desktop bundles the Docker CLI, Docker Engine, Docker Compose V2, Buildx, a VM backend, automatic updates, and a graphical settings app. Docker Engine is the server-focused Linux installation: packages such as docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, and docker-compose-plugin. Docker’s current Ubuntu instructions install from Docker’s official apt repository, not Ubuntu’s older docker.io package.
Docker Desktop licensing also matters in some workplaces. Docker Desktop is free for personal use, education, non-commercial open source, and small businesses under Docker’s published limits. Larger commercial organizations may need a paid subscription. Docker Engine on Linux is commonly used on servers and CI hosts, but you should still follow your organization’s policy.
Syntax
The installation itself depends on your operating system, but the verification commands are the same once Docker is installed:
docker --version
docker compose version
docker info
docker run --rm hello-world:latest
| Command | What it checks |
|---|---|
docker --version |
Confirms the Docker CLI is installed and reachable from your PATH. |
docker compose version |
Confirms Compose V2 is installed as the modern docker compose plugin. |
docker info |
Confirms the CLI can talk to a running daemon and prints server details. |
docker run --rm hello-world:latest |
Pulls a tiny test image, creates a container, runs it, prints a success message, and removes the stopped container. |
Use docker compose with a space. The old docker-compose command was the standalone Compose V1 binary and should not be used for new lessons or projects.
Examples
Example 1: Verify Docker Desktop on macOS or Windows
After installing Docker Desktop from Docker’s official installer and starting the application, open a terminal. On Windows, PowerShell is fine. On macOS, Terminal, iTerm, or another shell is fine.
docker --version
docker compose version
docker info
Output:
Docker version 29.0.0, build example
Docker Compose version v2.40.0
Client:
Context: desktop-linux
Server:
Containers: 0
Images: 0
The exact versions will differ, but three things should be true: the CLI prints a Docker version, Compose prints a V2 version, and docker info includes a Server section. If docker info says it cannot connect to the Docker daemon, Docker Desktop is probably not running yet, or your terminal is using the wrong Docker context.
Example 2: Install Docker Engine on Ubuntu from Docker’s Repository
On Ubuntu, use Docker’s official repository so you get current Docker Engine packages and the Compose V2 plugin. These commands set up Docker’s signing key and repository source:
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
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 update
Then install Docker Engine, the CLI, container runtime, Buildx, and Compose:
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl status docker
Output:
docker.service - Docker Application Container Engine
Loaded: loaded
Active: active (running)
This installs the official Docker packages and starts the docker systemd service on typical Ubuntu systems. If the service is not active, start it with sudo systemctl start docker. On servers, this service is the long-running daemon that receives requests from the CLI.
Example 3: Run the Test Image and a Real Web Container
The first run proves that the daemon can pull from a registry, create a container, run its command, and clean up afterward.
docker run --rm hello-world:latest
Output:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
The hello-world image exits immediately after printing text, so it is only a smoke test. A more realistic test is a web server that keeps running and publishes a port:
docker run -d --name install-nginx -p 8080:80 nginx:1.27-alpine
docker ps --filter name=install-nginx
docker stop install-nginx
docker rm install-nginx
Output:
8f3c2a1b4d5e
CONTAINER ID IMAGE STATUS PORTS NAMES
8f3c2a1b4d5e nginx:1.27-alpine Up 3 seconds 0.0.0.0:8080->80/tcp install-nginx
install-nginx
install-nginx
Visiting http://localhost:8080 should reach Nginx while the container is running. The -p 8080:80 flag is what publishes container port 80 to host port 8080. A Dockerfile’s EXPOSE instruction would only document a port; it would not publish it.
How It Works Step by Step
- You install Docker Desktop or Docker Engine. This places the
dockerCLI on your machine and configures a daemon endpoint. - On Linux, the daemon usually runs as the
dockersystemd service. On macOS and Windows, Docker Desktop starts a managed Linux VM and runs the daemon there. - When you run
docker run --rm hello-world:latest, the CLI sends an API request to the daemon through the current Docker context. - The daemon checks whether the image exists locally. If it does not, Docker contacts the configured registry, resolves the tag to a manifest, and downloads the missing layer blobs.
- Docker creates a container by mounting the image’s read-only layers and adding a thin writable layer. For
hello-world, the container runs one short process and exits. - Because
--rmwas used, Docker removes the stopped container. The image remains cached locally, so the next run is faster. - For the Nginx example, Docker also creates network plumbing and host port forwarding because you supplied
-p 8080:80.
This explains why installation problems often appear as connection errors, not syntax errors. If the CLI exists but the daemon is stopped, docker --version can succeed while docker run fails. If the daemon exists but your user cannot access its socket on Linux, commands may work with sudo but fail without it.
Common Mistakes
Installing an Unofficial or Old Package
sudo apt install docker.io docker-compose
This may install distribution-packaged Docker components that lag behind Docker’s official repository, and it may install the legacy standalone docker-compose binary. Prefer Docker’s official repository and install docker-compose-plugin so docker compose works.
Thinking the CLI Means the Daemon Is Running
docker --version
docker run --rm hello-world:latest
The first command only checks the client. The second requires the daemon. If it fails with a connection error, start Docker Desktop or run sudo systemctl start docker on a systemd-based Linux host.
Adding Yourself to the Docker Group Without Understanding the Risk
sudo usermod -aG docker "$USER"
newgrp docker
On Linux, this lets you run Docker without sudo, but membership in the docker group grants root-level control over the host through the Docker daemon. It is convenient for a developer workstation, but it is a serious security decision on shared machines and production servers.
Using latest as a Setup Habit
docker run --rm nginx:latest
This is acceptable for a quick experiment, but avoid building habits around latest. Tags can move. In examples, teams, and production notes, use a specific tag such as nginx:1.27-alpine so everyone is testing the same image version.
Best Practices
- Install Docker Desktop on macOS and Windows unless you have a specific server or advanced Linux requirement.
- Install Docker Engine from Docker’s official Linux repository on servers and Linux development hosts.
- Verify both the client and daemon with
docker --version,docker compose version,docker info, anddocker run --rm hello-world:latest. - Use
docker compose, not the legacydocker-composecommand, for new Compose work. - Prefer specific image tags in examples and real projects. Treat
latestas a moving label. - On Linux, consider the security impact before adding users to the
dockergroup. Rootless Docker may be a better fit in locked-down environments. - Keep Docker Desktop running before using the CLI on macOS or Windows, and check the active context if commands unexpectedly target the wrong daemon.
- Remember that Docker Desktop stores Linux containers and named volumes inside its managed VM, not as normal host folders you should edit directly.
Practice Exercises
- Install Docker for your operating system and run the four verification commands from the Syntax section. Expected end state:
docker infoshows aServersection andhello-worldprints its success message. - Start
nginx:1.27-alpinenamedpractice-install-weband publish it on host port8090. Hint: use-d,--name, and-p. - On a Linux test machine, decide whether your user should be added to the
dockergroup. Write down the tradeoff: convenience withoutsudoversus root-level daemon access.
Summary
- A complete Docker setup includes the CLI, a running daemon, container runtime components, Buildx, and modern Compose.
- Docker Desktop is the usual path on macOS and Windows because Linux containers run inside a managed Linux VM.
- Docker Engine is the standard Linux server installation and should come from Docker’s official package repository when possible.
docker --versionchecks the client;docker infoanddocker runprove the daemon is reachable.hello-worldverifies image pull, container creation, process execution, and cleanup.- Use
docker composeand specific image tags as your default habits from the beginning.
