Chapter 1: Foundations
Topic 06

Installing Docker and Your First Container

ToolingSetup

Getting Docker is two different stories depending on the host. On a Linux machine you install Docker Engine — the daemon, the CLI, and containerd — and containers run directly on that machine's kernel. On macOS and Windows you install Docker Desktop, which runs a lightweight Linux VM behind the same CLI, because Linux containers need a Linux kernel to share and your laptop does not have one.

Once it is running, a single command proves the whole client → daemon → runtime chain from topic 05 works end to end. docker run hello-world pulls an image, the daemon creates a container, runc starts the process, it prints a message and exits — the full round trip in one line.

Engine vs Desktop

Docker Engine is the daemon-on-the-host install: dockerd, containerd, and the docker CLI running directly on Linux, with containers on the machine's own kernel. It is what runs in production and CI, and it is free. Docker Desktop wraps a managed Linux VM, a GUI, Kubernetes, and assorted extras behind the same CLI on macOS, Windows, and optionally Linux — a developer convenience, not a server runtime.

Two ways to get Docker
Docker Engine
dockerd, containerd, and the CLI installed directly on Linux, with containers on the machine's own kernel. The production and CI runtime, and free.
Docker Desktop
A managed Linux VM plus a GUI behind the same CLI on macOS and Windows, supplying the Linux kernel those hosts lack. A developer convenience, not a server runtime.

The split matters for two reasons. On a Linux server, installing Desktop instead of Engine adds a VM and a GUI you neither need nor want under a production workload. And Docker Desktop carries licensing terms — it is not free for larger companies above a headcount and revenue threshold — that Engine does not. Check the current terms against your company's size before rolling Desktop out broadly.

The First Run, Step by Step

docker run hello-world does more than print a greeting. The daemon checks the local image store, finds nothing, and pulls the hello-world image from Docker Hub; it then creates a container from that image, runc starts the process, the process writes its message to stdout and exits, and the container stops. That one command exercises the registry round-trip, the pull, the create, and the start — the entire chain in a single invocation.

If hello-world prints its message, the install is sound: the CLI reached the daemon, the daemon reached the registry and containerd, and runc started a process. It proves the plumbing works — and nothing more. It says nothing about whether your storage, networking, or security are configured for real workloads, so treat a clean run as a smoke test, not a green light for production.

An Interactive Container

docker run -it ubuntu bash is where topic 03 becomes visible. The -it flags attach an interactive terminal, and instead of exiting, the container drops you into a Bash shell inside it. Run ps aux there and you will see two or three processes, not the host's hundreds, because the PID namespace hides them. Run hostname and you get a random twelve-character ID, the container's own.

Inside the ubuntu container — isolation made visible
# the shell is now inside the container
root@3f2a1b9c4d5e:/# ps aux
USER  PID  COMMAND
root    1  bash          # the app is PID 1 — the PID namespace
root   10  ps aux
root@3f2a1b9c4d5e:/# hostname
3f2a1b9c4d5e            # a random container ID, not the host's name

Everything topic 03 described is now in front of you: Bash is PID 1, the process list is nearly empty, and the hostname is the container's own. Type exit and the shell's process ends, which ends PID 1, which stops the container — proof one more time that a container is a process, and that killing its main process kills the container.

Images Are Cached Locally

Run the same image a second time and the pull is gone — the container starts immediately. The daemon already has the image's layers in its local store, so there is nothing to download; this is your first sight of the local image cache that Chapter 2 covers in depth. docker images lists what is cached, with each image's repository, tag, digest, and size.

Rootless and Sudo

On Linux, the CLI talks to the daemon over a socket that only root and the docker group can reach, so a bare docker run as a normal user fails with a permission error. The two fixes are to prefix every command with sudo, or to add your user to the docker group so the socket is reachable without it. The second is more convenient and exactly as privileged — group membership grants root-equivalent access, as topic 05 spelled out.

When neither a root daemon nor a root-equivalent group is acceptable — a shared build host, a hardened CI runner — rootless Docker runs the daemon as an unprivileged user instead. Chapter 10 covers it in full; here it is enough to know the option exists and is the answer when "just add yourself to the docker group" is too much authority to hand out.

Common Mistakes
  • Installing Docker Desktop on a production Linux server when Docker Engine is the correct install — Desktop is a developer tool with a VM, a GUI, and licensing implications, not a server runtime, and it wastes resources on a host.
  • Running every docker command with sudo out of habit while not realizing the docker group grants the same root-equivalent access more conveniently — same authority, just less typing, and the same risk either way.
  • Assuming a clean docker run hello-world means Docker is production-ready — it proves the chain works end to end and nothing about storage, networking, or security being configured for real workloads.
  • On macOS or Windows, being surprised that containers cannot see host processes or that bind-mount I/O is slower — there is a Linux VM in between, and file I/O crossing that VM boundary is an expected, not anomalous, cost.
Best Practices
  • Install Docker Engine on Linux servers and CI runners, and reserve Docker Desktop for developer laptops where the managed VM and GUI actually earn their keep.
  • Add your user to the docker group on a development machine for convenience, but treat that membership as granting root and restrict who gets it accordingly.
  • Verify a new install with docker run hello-world and then an interactive ubuntu shell, so you confirm both the registry round-trip and the namespace isolation in one sitting.
  • Check Docker Desktop's licensing terms against your company's size before deploying it broadly, since it is not free for all commercial use above its headcount and revenue threshold.
Comparable tools Podman a Docker-compatible CLI installable without Desktop's licensing colima · Rancher Desktop run the Linux VM on macOS as Desktop alternatives containerd · nerdctl the daemon-included, Docker-free path on Linux

Knowledge Check

When should you install Docker Engine rather than Docker Desktop?

  • On Linux servers and CI runners, where you want the daemon on the host without a VM, GUI, or Desktop licensing
  • On macOS and Windows developer laptops alike, where the bundled lightweight Linux VM is what is required to run any containers at all
  • Whenever you want a built-in graphical dashboard to browse and manage your containers and images visually
  • Whenever you need a one-click local single-node Kubernetes cluster bundled directly with the install

What does a successful docker run hello-world actually confirm?

  • That the pull, daemon, and runtime chain works end to end — not that storage, networking, or security are configured
  • That Docker is now fully configured, hardened, and ready to take on real production workloads at scale
  • That your user account has now been granted privileged kernel access for every container you run from this point on
  • That your custom container networks, bridges, and named volumes are all correctly wired up and working as configured

Inside docker run -it ubuntu bash, ps shows only two or three processes. Why?

  • The PID namespace gives the container its own process tree, so it sees only its own processes, not the host's
  • Docker pauses all the host's other processes while an interactive container is attached
  • The ubuntu container boots its own minimal stripped-down guest operating system with its own separate kernel, and that pared-back guest simply has almost no background processes to start
  • The ps command is broken inside containers and truncates its output to a few lines

Why is adding your user to the docker group equivalent to granting root on the host?

  • The group grants access to the root-owned daemon socket, and through it you can run a container that edits any host file
  • Joining the group silently rewrites your own user ID to 0 for every single command you go on to run on the host
  • It directly exposes the hashed login passwords of every other user account that is also a member of the same docker group on the host
  • It only lets you skip typing sudo in front of one single one-off docker command at a time, and nothing more

You got correct