Installing Docker: Desktop, Engine on Linux, and rootless
Install Docker the right way for your machine: Docker Desktop on macOS and Windows, Docker Engine from Docker's repository on a Linux server, and rootless mode when the docker group is too much power.
By the end of this lesson you'll be able to
0 of 5 completeWhat you're installing
"Docker" is several programs. The docker command you type is only a client; the work happens in a
daemon it talks to:
docker version shows both halves. If the Server part is missing, the CLI can't reach the daemon:
$ docker version Client: Version: 29.1.3 API version: 1.52 Context: default Server: Engine: Version: 29.1.3 API version: 1.52 (minimum version 1.44) containerd: Version: 2.2.2 runc: Version: 1.4.0-0ubuntu1
docker versiondockerbuild and run containersversionclient and server versions
Docker Desktop: macOS and Windows
On a laptop, install Docker Desktop from docker.com. It brings the CLI, Compose, Buildx, a small Linux
VM to run containers in (WSL 2 on Windows, which you enable first), and a GUI to see containers, images and
logs. Files, ports and DNS are forwarded between your laptop and the VM, so localhost:8080 works as you'd
expect.
Two things to know:
- Licensing: Docker Desktop is free for personal use, education, non-commercial open source and small businesses (fewer than 250 employees and under $10 million in yearly revenue). Larger companies need a paid subscription. Docker Engine on Linux has no such licence.
- Architecture: on Apple silicon Macs, images are
arm64by default. An image built only foramd64runs slowly under emulation, and a laptop build may not match anamd64server. Build with--platform linux/amd64when the target is a typical cloud server.
Docker Engine on a Linux server
On servers, install Docker Engine from Docker's own repository (the Linux course's Packages lesson explains repositories and signing keys):
$ sudo install -m 0755 -d /etc/apt/keyrings $ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null $ sudo apt-get update $ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin $ sudo systemctl enable --now docker
sudo install -m 0755 -d /etc/apt/keyringssudorun the next command as root
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asccurlmake an HTTP request-owrite the body to this file
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \echoprint text
sudo systemctl enable --now dockersystemctlcontrol systemd services
Ubuntu's own docker.io package is an alternative (sudo apt-get install docker.io docker-buildx docker-compose-v2): simpler, sometimes a version or two behind. Note that Compose is a separate package
either way; without it, docker compose answers unknown command.
Check the install:
$ docker run --rm hello-world 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. (amd64) 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. $ docker info | grep -E "Storage Driver|Cgroup Version|Docker Root Dir" Storage Driver: overlayfs Cgroup Version: 2 Docker Root Dir: /var/lib/docker
docker run --rm hello-worlddockerbuild and run containersrunstart a new container--rmdelete the container when it exitshello-worldthe image to start
docker info | grep -E "Storage Driver|Cgroup Version|Docker Root Dir"infohow the daemon is set up|pipe: send output to the next commandgrepfind lines that match a pattern
Those four steps are the diagram above, in words.
Running docker without sudo
$ ls -l /var/run/docker.sock srw-rw---- 1 root docker 0 Oct 14 09:03 /var/run/docker.sock $ sudo usermod -aG docker "$USER" # then log out and in again $ docker ps # works without sudo in a new login
ls -l /var/run/docker.socklslist files-llong format: permissions, owner, size, date
sudo usermod -aG docker "$USER"sudorun the next command as root
docker psdockerbuild and run containerspslist running containers
Until you log in again, your shell keeps its old groups and gets permission denied while trying to connect to the docker API at unix:///var/run/docker.sock.
Rootless mode
Rootless mode runs the daemon and every container as your normal user. Root inside a container is mapped to your unprivileged user outside, so a container escape gets your rights, not the host's:
$ sudo apt-get install -y uidmap docker-ce-rootless-extras $ dockerd-rootless-setuptool.sh install # as your normal user, not with sudo $ docker context use rootless $ docker info | grep -i rootless
sudo apt-get install -y uidmap docker-ce-rootless-extrassudorun the next command as root
docker context use rootlessdockerbuild and run containerscontextchoose which daemon to talk to
docker info | grep -i rootlessinfohow the daemon is set up|pipe: send output to the next commandgrepfind lines that match a pattern-iignore upper/lower case
Trade-offs: ports below 1024 need an extra setting, some storage and network features are slower, and each user has their own images and containers. Podman runs rootless by default and accepts the same commands, which makes it a common choice on shared hosts.
Install Docker and prove every part works
On an Ubuntu VM, install Docker Engine from Docker's repository with the Compose and Buildx plugins, let your user run docker without sudo, and prove the client, daemon, containerd, storage driver, cgroups and image pulls all work.
hello-world output, docker version with both halves, and docker compose versionReveal one safe solution
$ sudo install -m 0755 -d /etc/apt/keyrings $ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \ | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null $ sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin $ sudo usermod -aG docker "$USER" && exit # log in again $ docker run --rm hello-world | head -2 Hello from Docker! This message shows that your installation appears to be working correctly. $ docker version --format 'client {{.Client.Version}} · server {{.Server.Version}}' $ docker info --format 'storage={{.Driver}} cgroup={{.CgroupVersion}}' storage=overlayfs cgroup=2 $ docker compose version && docker buildx version
sudo install -m 0755 -d /etc/apt/keyringssudorun the next command as root
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asccurlmake an HTTP request-owrite the body to this file
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \echoprint text
sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin&&run the next command only if this one worked
docker run --rm hello-world | head -2dockerbuild and run containersrunstart a new container--rmdelete the container when it exitshello-worldthe image to start|pipe: send output to the next commandheadshow the first lines
docker version --format 'client {{.Client.Version}} · server {{.Server.Version}}'versionclient and server versions--format 'client {{.Client.Version}} · server {{.Server.Version}}'print only these fields (Go template)
docker info --format 'storage={{.Driver}} cgroup={{.CgroupVersion}}'infohow the daemon is set up--format 'storage={{.Driver}} cgroup={{.CgroupVersion}}'print only these fields (Go template)
docker compose version && docker buildx versioncomposerun a multi-container app from compose.yamlbuildxthe BuildKit builder
If docker version prints only the client, run systemctl status docker; if it says permission denied,
your new group hasn't applied yet: log out and in again.
Try it yourself
0 of 6 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
Why does Docker Desktop on macOS run a Linux virtual machine?
Official reading for this lesson
Or get every quiz answer right and it completes itself.