Chapter 02Lesson 02~120 minutes

Installing Docker Engine and Desktop, CLI Basics, Daemon Connectivity, Contexts, and Environment Setup: Guided Hands-On Workflow and Core Operations

Install and verify Docker through a reviewable workflow on a disposable Linux host, then map the same evidence model to Docker Desktop: inspect platform and package source, install official packages, prove client/server/context identity, run immutable test content, restart safely, and verify persistent state.

Hands-onOfficial packageshello-worldDaemon restartDesktop

Learning objectives

  • Configure Docker's official apt repository and explain the signing/repository evidence before installation.
  • Install the Engine, CLI, containerd package, Buildx plugin, and Compose plugin while recording actual resolved versions.
  • Verify service state, client/server/API identity, active context, data root, storage/cgroup/security baseline, and CLI plugins.
  • Resolve a test image to its repository digest, run a bounded labeled container, and distinguish the successful states it proves.
  • Restart a disposable daemon or Desktop safely and verify which Docker state persists without broad cleanup.
Chapter 02 technical baseline — verified 2026-09-20. Docker Engine 29.8.1 and Docker Desktop 4.91.0 are current releases at this verification date. Current upstream Buildx, BuildKit, and Compose releases are 0.37.1, 0.33.0, and 5.5.1 respectively. Do not infer those exact bundled versions from the product name: every lab records the actual client/server/API/context/storage/component baseline first.

1. Guided workflow scope

The mandatory path uses a disposable Ubuntu 24.04 or 26.04 Linux VM because it exposes the installation layers directly: package repository, system service, Unix socket, data root, and daemon restart. Docker's current Ubuntu documentation supports both releases. A Docker Desktop verification path follows later for Windows/macOS/Linux learners who already use Desktop.

Do not run this installation workflow on a production/shared host. Package removal, daemon installation, service restart, and group changes alter host-level state. Use a VM or machine you are authorized to administer. Preserve existing Docker data if the machine is not disposable.

2. Preflight the host before changing packages

set -eu

printf 'UTC: '; date -u +%Y-%m-%dT%H:%M:%SZ
uname -a
uname -m
cat /etc/os-release
command -v docker || true
docker --version 2>/dev/null || true
systemctl status docker --no-pager 2>/dev/null || true

dpkg --get-selections 2>/dev/null | grep -E '^(docker|containerd|runc|podman-docker)' || true

Confirm a supported 64-bit Ubuntu release/architecture. If Docker already exists, stop and inventory it rather than blindly uninstalling. The official installation guide lists conflicting packages because mixing independently packaged Docker/containerd/runc stacks can produce ownership and compatibility ambiguity.

3. Configure Docker's signed apt repository explicitly

This follows the current Docker-maintained Ubuntu installation path. The repository URI, suite, architecture, and signing key are visible rather than hidden behind a convenience script.

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
apt-cache policy docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

The final command is evidence: it shows which repository provides each candidate package. If a corporate mirror/proxy is required, document that boundary rather than disabling TLS verification.

4. Inspect available versions before installation

For a training VM you can install the current stable candidate, but production automation should usually make version selection explicit and test upgrades before rollout. Do not copy a stale package-version string from a tutorial because Ubuntu release suffixes differ.

apt list --all-versions docker-ce 2>/dev/null | sed -n '1,12p'
apt-cache policy docker-ce | sed -n '1,20p'

# Capture the candidate chosen by apt for your actual release.
ENGINE_CANDIDATE="$(apt-cache policy docker-ce | awk '/Candidate:/{print $2}')"
printf 'candidate=%s
' "$ENGINE_CANDIDATE"

5. Install the Engine, CLI, managed containerd package, Buildx, and Compose plugins

sudo apt install -y   docker-ce   docker-ce-cli   containerd.io   docker-buildx-plugin   docker-compose-plugin

sudo systemctl status docker --no-pager
systemctl is-enabled docker || true
systemctl is-active docker

The packages solve different problems. docker-ce-cli provides the client, docker-ce provides Engine packaging, containerd.io supplies the containerd/runtime bundle selected by Docker packaging, and the Buildx/Compose packages install CLI plugins. Do not assume plugin versions from the Engine number; record them.

6. Capture the first complete client/server baseline

sudo docker version
sudo docker info
sudo docker context ls
sudo docker context inspect default
sudo docker buildx version
sudo docker compose version

sudo docker info --format 'DockerRootDir={{.DockerRootDir}}'
sudo docker info --format 'Driver={{.Driver}} CgroupVersion={{.CgroupVersion}} CgroupDriver={{.CgroupDriver}}'
sudo docker info --format 'SecurityOptions={{json .SecurityOptions}}'

At this point the installation is usable through explicit elevation. That is a valid baseline. Do not rush to make non-root access work before deciding whether docker-group access or rootless mode is appropriate.

7. Resolve a verification image and run it by digest

The familiar hello-world check validates several layers: daemon connectivity, registry resolution/pull, local image content, container creation, runtime start, stdout capture, and exit handling. It does not validate application networking, persistent storage, or production security.

LAB_CONTAINER="da-ch02-install-check"
LAB_LABEL="devops-academy.lab=chapter02"

sudo docker pull hello-world:latest
HELLO_REF="$(sudo docker image inspect hello-world:latest --format '{{index .RepoDigests 0}}')"
printf 'resolved image=%s
' "$HELLO_REF"

sudo docker run --name "$LAB_CONTAINER" --label "$LAB_LABEL" "$HELLO_REF"
sudo docker inspect "$LAB_CONTAINER" --format   'id={{.Id}} image={{.Image}} status={{.State.Status}} exit={{.State.ExitCode}}'
sudo docker logs "$LAB_CONTAINER"

The tag is discovery input; the repository digest is the immutable content reference captured for the lab. The stopped container remains intentionally so the restart test has an object whose metadata should persist.

8. Restart the daemon and prove what persisted

Disposable-host action. Restarting Docker can affect running containers. This lab assumes no unrelated workload is on the VM. On a shared host, do not restart the daemon merely for training.
BEFORE_ID="$(sudo docker inspect "$LAB_CONTAINER" --format '{{.Id}}')"
BEFORE_ROOT="$(sudo docker info --format '{{.DockerRootDir}}')"

sudo systemctl restart docker
sudo systemctl is-active docker

AFTER_ID="$(sudo docker inspect "$LAB_CONTAINER" --format '{{.Id}}')"
AFTER_ROOT="$(sudo docker info --format '{{.DockerRootDir}}')"

printf 'container before=%s
container after =%s
' "$BEFORE_ID" "$AFTER_ID"
printf 'data root before=%s
data root after =%s
' "$BEFORE_ROOT" "$AFTER_ROOT"
sudo docker image inspect "$HELLO_REF" --format 'image still present: {{.Id}}'

The daemon process restarted, but persistent metadata/images survived because they belong to persistent daemon state. This is different from a running container's process state. Later chapters cover restart policies, live restore, and recovery in depth.

9. Decide how non-root access will work

For this lab you may continue with sudo docker. If you add your user to the docker group, record it as a privilege grant, not as “unprivileged Docker.” If your goal is actual daemon privilege reduction, follow the rootless-mode path and verify its prerequisites instead.

getent group docker || true
id
ls -l /var/run/docker.sock 2>/dev/null || true

# Rootless readiness inspection only; no mutation yet.
command -v newuidmap || true
command -v newgidmap || true
grep "^${USER}:" /etc/subuid /etc/subgid 2>/dev/null || true

Chapter 26 covers rootless Docker comprehensively. The important Chapter 02 skill is recognizing that socket access and rootless architecture are different decisions.

10. Docker Desktop verification path

If you use Docker Desktop instead of the disposable Linux VM, do not copy systemd or /var/lib/docker assumptions into the host OS. Verify Desktop's own boundary and the selected context.

docker version
docker info
docker context ls
docker context show
docker context inspect "$(docker context show)"
docker buildx version
docker compose version

On Docker Desktop for Linux, desktop-linux identifies the Desktop VM target while default can refer to a native host Engine. On Windows with WSL 2, use wsl --version and Desktop settings to record the backend. Docker's current Windows documentation requires WSL 2.1.5 or later for the WSL backend and supports Windows 10 22H2 build 19045 and supported Windows 11 releases; recheck those requirements when the lab is run later.

11. Challenge — explain a successful hello-world precisely

Write a five-sentence explanation using your actual evidence. It must identify: selected context/endpoint; client and server versions; resolved image digest; container ID/exit code; and which state persisted after daemon/Desktop restart. Then add one sentence explaining what this test does not prove.

12. Guarded cleanup

sudo docker inspect "$LAB_CONTAINER" --format 'name={{.Name}} labels={{json .Config.Labels}}'

# Remove only the exact Chapter 02 lab container after verifying identity.
sudo docker rm "$LAB_CONTAINER"

# Keep the pulled image unless this disposable VM specifically requires a clean slate.
sudo docker ps -a --filter "label=$LAB_LABEL"

Do not use docker system prune -a --volumes to clean a one-container installation lab. Broad cleanup is not evidence-driven ownership.

Knowledge check

Why does the lab capture apt-cache policy before installation?

A hello-world container exits with code 0. What does that prove?

Why does the lab keep the stopped container through a daemon restart?

Why is sudo docker an acceptable training baseline before adding the user to the docker group?

On Docker Desktop for Linux, why can docker context use default and docker context use desktop-linux show different images?

Summary

You installed Docker through an inspectable package path, verified the service and actual client/server/component baseline, resolved a test image to immutable content, proved a bounded container lifecycle, and separated daemon restart from persistent Docker state. You also kept privilege and Desktop/native boundaries explicit. The next lesson turns those facts into installation design choices rather than one universal recipe.

Next lesson

Next: Installing Docker Engine and Desktop, CLI Basics, Daemon Connectivity, Contexts, and Environment Setup: Configuration, Design Choices, and Tradeoffs

Continue with the next lesson in the course sequence and carry forward the evidence-first Docker operating model.

Official references and version notes

Version-sensitive statements in this chapter were rechecked against primary documentation on 2026-09-20. Installation support, package names, Desktop requirements, component versions, context behavior, rootless prerequisites, and security guidance evolve. Always record the versions and platform facts actually reported by the environment you are operating.

Current baseline, not a frozen requirement

At this chapter's verification date, Docker Engine 29.8.1 and Docker Desktop 4.91.0 are current releases, while Buildx 0.37.1, BuildKit 0.33.0, and Compose 5.5.1 are current upstream releases. Bundled component versions can differ by Engine/Desktop/package source. Use docker version, docker info, docker buildx version, and docker compose version as execution evidence rather than assuming those upstream versions are installed.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.