Skip to content

Intro & Concepts — Module Overview

Every developer has heard it: “It works on my machine.” The application runs perfectly in development, then breaks in staging or production because of a different OS, a mismatched library version, or a missing environment variable. Docker narrows that gap by packaging your application together with everything it needs — the runtime, dependencies, and configuration — into a single, portable unit called a container.

With Docker you get:

  • Consistent environments — the same image runs the same userland on a laptop, a CI server, and a cloud VM (the kernel underneath is still the host’s, see Containers vs VMs).
  • Isolation — each container has its own filesystem view and process space, so applications do not interfere with each other.
  • Speed — no guest OS to boot. Measured below: a warm docker run finishes in about a quarter of a second.
  • Reproducibility — a Dockerfile is a precise recipe; building it against the same pinned base image and inputs gives the same result.
LessonWhat you will learn
This pageProblems Docker solves, first container, real timings
What is Docker?Architecture: client, daemon, images, registries — proven over the real socket
Containers vs VMsKernel sharing, namespaces, cgroups — proven with uname, /proc and memory.max
Install & VerifyInstalling Docker, verifying installation, the docker group
Your First ContainerInteractive + detached containers, docker ps, docker stop and the 10-second timeout

Every command and every output block in this course was run on one machine, on 2026-09-30:

ComponentVersion (from docker version / docker compose version)
Docker Desktop4.81.0 (macOS, Apple Silicon, linux/arm64)
Docker Engine29.6.1 (API 1.55, minimum 1.40)
containerd / runcv2.2.5 / 1.3.6
Buildx / BuildKitv0.35.0-desktop.2 / v0.31.1
docker compose pluginv5.3.0 (there is no separate docker-compose binary and no “Compose v2” any more)

Outputs are pasted as they came out of this Mac. Where Linux Engine behaves differently, a callout says so. Image sizes are arm64 sizes; amd64 sizes differ a little.

The fastest way to confirm Docker is working is the official hello-world image. Docker pulls the image from Docker Hub if it is not cached locally, starts a container, prints a message, and exits.

Terminal window
docker run hello-world

Try it now using the interactive playground below:

docker run hello-world

Timing three runs of a container that does nothing (true exits immediately), image already present, on this Mac (wall clock including the CLI start-up and the round trip into the VM):

$ docker run --rm alpine:3.24 true # 0.25 s
$ docker run --rm alpine:3.24 true # 0.24 s
$ docker run --rm alpine:3.24 true # 0.25 s

And a truly cold start — image not on disk, so it includes the pull from Docker Hub (docker rmi hello-world first):

$ docker run --rm hello-world # 4.21 s (mostly the network pull)
$ docker run --rm hello-world # 0.25 s (warm)

So “milliseconds” in marketing means: creating the namespaces and cgroups and exec-ing the process is cheap, and there is no kernel to boot. What you actually wait for is the pull (first time) and your own application’s start-up. A VM has to boot a whole guest kernel and init system before your process even starts; that difference is the point, not the exact number.

flowchart LR
  subgraph MAC["macOS (your Mac)"]
    cli["docker CLI (darwin/arm64)"]
    sock["~/.docker/run/docker.sock"]
  end
  subgraph VM["Docker Desktop Linux VM (linuxkit kernel)"]
    engine["dockerd + containerd + runc"]
    c1["your containers (linux/arm64)"]
  end
  cli --> sock --> engine --> c1
What runs where on Docker Desktop for Mac

AI assistants write plausible “is Docker ready?” checks. This one is used at the top of many CI and setup scripts:

🐛 Spot the bug

#!/bin/sh
# AI: "verify that Docker is installed and running before the build"
if docker --version; then
echo "Docker is ready"
docker build -t myapp:ci .
fi
Show the answer

docker --version only asks the client binary for its version, which is why it works even when no daemon is reachable. Proven here by pointing the CLI at a socket that does not exist:

$ DOCKER_HOST=unix:///nonexistent.sock docker --version
Docker version 29.6.1, build 8900f1d
$ echo $?
0
$ DOCKER_HOST=unix:///nonexistent.sock docker version --format '{{.Server.Version}}'
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
$ echo $?
1

The script prints “Docker is ready” and only then fails on the docker build, with a misleading log. Ask the server something instead — docker info or docker version --format both exit non-zero when the daemon is unreachable:

scripts/check-docker.sh
#!/bin/sh
set -eu
if ! docker info >/dev/null 2>&1; then
echo "Docker daemon is not reachable (is Docker Desktop running?)" >&2
exit 1
fi
echo "Docker is ready: $(docker version --format '{{.Server.Version}}')"

Think of Docker as three things stacked: a client that turns your commands into API calls, an engine that owns images, containers, networks and volumes, and the Linux kernel that actually isolates processes. A container is not a small computer; it is a normal Linux process that the kernel has been told to show a private view of the filesystem, network and process table. That is why it starts in about a quarter of a second, and why it can never be faster than the process inside it.

ChoiceBenefitCost
Containers instead of “install on the host”Same image everywhere, clean throw-away environmentsYou now operate an engine, images and a registry
Docker Desktop on Mac/WindowsOne installer gives you CLI, engine, Compose, BuildxEngine runs in a VM: extra RAM, file-sharing overhead, bridge IPs not reachable from the host
Pulling from Docker Hub on first runZero setupCold start costs seconds and counts against pull rate limits
  • “A container starts in milliseconds.” The isolation set-up is cheap, but measured end-to-end it was 0.25 s warm and 4.21 s cold here. Your application’s own start-up usually dominates.
  • “Docker guarantees identical behaviour everywhere.” The image is identical; the kernel, CPU architecture (arm64 here) and host settings are not. An amd64-only image runs under emulation on Apple Silicon.
  • “docker --version proves Docker works.” It only proves the client is installed. Use docker info.
What problem does Docker primarily solve?
What is a Docker container?
Which Linux kernel features provide container isolation?
On a Mac, docker version shows OS/Arch: darwin/arm64 for the client and linux/arm64 for the server. What does that tell you?
A CI script runs if docker --version; then ... docker build ...; fi to check that Docker is ready. What is wrong with it?