How to Install Docker on Different Operating Systems
Table of Contents
The installation steps differ by OS, but the more useful thing to understand first is why they differ. Docker’s container runtime is a Linux technology: it relies on Linux kernel namespaces and cgroups. On Linux, Docker runs natively. On macOS and Windows, Docker runs inside a lightweight Linux virtual machine under the hood, which explains some of the performance and file-sharing quirks unique to those platforms.
Linux #
Install Docker Engine from Docker’s official repository rather than from your distribution’s own package, which is often outdated. The quickest way is Docker’s convenience script:
# Works on most common distributions
curl -fsSL https://get.docker.com | sh
# Run docker without sudo (log out and back in afterwards)
sudo usermod -aG docker $USER
On distributions that don’t start services automatically after installing a package (Fedora and CentOS, for example), also run sudo systemctl enable --now docker.
For production or anything beyond a personal machine, install from Docker’s official apt or yum repository instead. The steps for each distribution are in the Docker Engine installation guide. That way upgrades go through your normal patch management rather than a shell script from the internet.
The usermod -aG docker step is worth understanding, not just running. Membership in the docker group is effectively root-equivalent, since the Docker daemon runs as root and anyone who can talk to it can mount the host filesystem into a container. Don’t add untrusted users to that group.
macOS #
Install Docker Desktop, either with Homebrew or with the installer from Docker’s website:
brew install --cask docker
Docker Desktop is free for personal use, education and small businesses, but larger companies need a paid subscription (check the current terms). Alternatives such as Colima (open source) or OrbStack (commercial, with a free tier for personal use) are popular, and many people find them lighter on resources.
Under the hood, Docker Desktop on a Mac starts a small Linux VM (typically using Apple’s Virtualization framework) to host the actual Docker daemon and containers. There is no native macOS container runtime, because containers need a Linux kernel. Two quirks follow from that.
Bind mounts can be slow #
Every file read or write on a bind-mounted directory crosses the VM boundary. For projects with heavy file watching (a large node_modules folder, hot-reload dev servers), this can be the biggest source of “Docker feels slow” complaints on a Mac.
Host networking behaves differently #
With --network host on Linux, a container shares the host’s network. On a Mac the “host” is the Linux VM, not your Mac, so by default that doesn’t give a container access to services running on your Mac. Containers reach those through the special hostname host.docker.internal instead.
Windows #
Docker Desktop on Windows can use two different backends.
WSL2 backend #
This is the recommended option and has been the default for a long time. Containers run inside the WSL2 Linux VM, which gives near-native Linux performance for anything that also lives inside the WSL2 filesystem. It works on Windows Home as well as Pro, and WSL2 itself is installed by running wsl --install in an administrator PowerShell.
Hyper-V backend #
This is an older backend that requires Windows Pro or Enterprise. Today you mostly run into it in connection with Windows containers (actual Windows-based container images), which are a separate ecosystem from Linux containers and considerably less common.
For Linux containers on Windows, which is the overwhelming majority of real-world use, install WSL2 first and then Docker Desktop with the WSL2 backend enabled. Keep your project files inside the WSL2 filesystem (for example in your home directory inside your WSL distribution) rather than on the Windows C:\ drive, which WSL sees as /mnt/c. Crossing the Windows/WSL boundary for file I/O carries a similar performance penalty to crossing the VM boundary on a Mac.
Verifying the install #
The check is the same everywhere:
docker run hello-world
#> Unable to find image 'hello-world:latest' locally
#> latest: Pulling from library/hello-world
#> 4f55086f7dd0: Pull complete
#> Digest: sha256:c567b231f65614688711a75f31518a064258a51d442117a3e771a3a8fdba78ef
#> Status: Downloaded newer image for hello-world:latest
#>
#> 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.
#>
#> To try something more ambitious, you can run an Ubuntu container with:
#> $ docker run -it ubuntu bash
#>
#> Share images, automate workflows, and more with a free Docker ID:
#> https://hub.docker.com/
#>
#> For more examples and ideas, visit:
#> https://docs.docker.com/get-started/
If that pulls the image and prints the message above, you have a working Docker installation that can reach a running daemon and pull from a registry. When Docker doesn’t work right after an install, the cause is usually one of two things rather than the installation itself: the daemon isn’t running, or your user lacks permission to reach its socket.