#

Docker

Subscribe
Docker October 29, 2026·9 min read

How to Install Docker Engine on Ubuntu

On a supported Ubuntu release, install Engine, Compose and Buildx from Docker's official APT repository, then verify the daemon with a test container. If the server already runs containers, or your policy requires Ubuntu-managed packages, deal with that before changing the package source. Once the checks pass, set up user access and published ports before running your first application.

Before you install: check Ubuntu and existing Docker

Docker Ubuntu preflight

Start by finding your Ubuntu release and checking whether Docker already runs anything you want to keep. A fresh VPS and a server with active containers need different preparation before installing upstream Docker Engine.

The Docker Ubuntu installation guide lists these releases:

Ubuntu releaseCodename
26.10Stonking
26.04 LTSResolute
24.04 LTSNoble
22.04 LTSJammy

Listed architectures include amd64 (x86_64), armhf, arm64, s390x and ppc64le. This procedure is for Ubuntu itself. Derivatives such as Linux Mint need a separate compatibility check.

. /etc/os-release
printf '%s\n' "$PRETTY_NAME"
dpkg --print-architecture

Compare the output with the list above. Next, see which Docker-related packages are installed and how Docker got onto the machine:

command -v docker
dpkg -l docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc docker-ce docker-ce-cli 2>/dev/null

If the host uses Snap, check its Docker installation through Snap as well. On a machine in use, write down the containers, volumes, bind mounts and services that use Docker before changing any packages.

Existing Docker workloads

An r/docker user moving an Ubuntu Server from Snap to APT in September 2022 said, “Up until now I didn't really know I was running them in snap vs apt.”

Docker flags several packages as conflicts, including docker.io, docker-compose, docker-compose-v2, docker-doc, docker-buildx, podman-docker, containerd and runc. Resolve any that are installed before switching to Docker's upstream package family.

  • Fresh or cleared host: If the machine has no workloads to keep, remove only the conflicting packages that are actually installed. If the inspection shows docker.io and standalone containerd, for example, run sudo apt remove docker.io containerd. Read APT's proposed changes before accepting them.
  • Occupied host: Stop the migration until you know what uses Docker and have a backup or rollback plan that covers those workloads. Removing packages can interrupt running services, and a copy of /var/lib/docker/ alone isn't a verified backup.

Choose the right installation method

Docker package options

Pick the package source before running any installation command. Docker CE from Docker's repository and Ubuntu's docker.io are separate packaging and update paths.

MethodUse whenTrade-off
Official Docker APT repositorySupported Ubuntu host that needs upstream Engine and pluginsAdds Docker's signed third-party repository
Ubuntu docker.ioYour policy requires distribution-managed packagesVersions and packaging can differ from upstream Docker CE
Manual Docker .deb filesYou can't use the repository and manage packages by handYou upgrade packages by hand
Docker convenience scriptDevelopment or test provisioningDocker doesn't recommend it for production

The steps below use the official APT route. It installs a matched package family for Engine, CLI, container runtime, Compose and Buildx, and APT handles later updates.

Docker installation choices

If your policy requires distribution packaging, docker.io is a fine choice.

Set up Docker's official APT repository

Docker APT repository

Docker ships its upstream Ubuntu packages through a signed APT source. Before creating it, check for an older Docker repository entry:

grep -Rni 'download.docker.com/linux/ubuntu' /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null

On a fresh system, no match is normal. On an existing installation, compare the entries in .list and .sources files, signing-key settings included, and keep a single definition before adding the new source. An odd filename alone is no reason to delete an entry.

In August 2026, an r/selfhosted user setting up Docker on Ubuntu 26.04 hit conflicting signing-key values and asked, “What do these error messages mean? Should I try another fresh os install?” That was an APT source conflict, so the repository declarations were the place to look.

Install the packages you need to fetch the key:

sudo apt update
sudo apt install ca-certificates curl

Create the keyrings directory and put Docker's signing key in it:

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
Signed Docker packages

Write Docker's Deb822 source file:

sudo tee /etc/apt/sources.list.d/docker.sources <<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

Suites takes your Ubuntu codename and Architectures takes your APT architecture. Signed-By points the source at Docker's key.

sudo apt update

The update has to load the repository metadata cleanly before APT can install Docker packages. If it fails to download, check your access to the repository.

Install Docker Engine, Compose and Buildx

Docker Engine installation

Once the signed repository works and the conflicting packages are gone, install the full upstream package group:

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

docker-ce is the Engine, docker-ce-cli is the command-line client, and containerd.io is the matching container runtime. The other two packages add the docker compose and docker buildx plugins.

If APT reports that docker-ce has no installation candidate, look at your repository source and the command you pasted.

Verify that Docker and its plugins work

Docker runtime checks

Check the client, the daemon, a test container and the plugins separately.

docker --version

That confirms the client responds. The daemon runs as a separate service:

sudo systemctl status docker

On a standard systemd install the service should show as active. If it's inactive and this is the daemon you want to use, start it:

sudo systemctl start docker

Then have the daemon run Docker's test container:

sudo docker run hello-world

A successful run prints Docker's confirmation message and exits. If the image isn't on the machine yet, Docker pulls it from a registry, so a pull failure points to a network or registry problem.

Docker component verification

Last, check both plugins:

docker compose version
docker buildx version
ComponentCheckWhat it tells you
Docker CLIdocker --versionThe client runs
DaemonDocker service statusThe systemd-managed Engine service is active
Container executionhello-worldThe daemon can run a test container
Composedocker compose versionThe Compose plugin is available
Buildxdocker buildx versionThe Buildx plugin is available

Run Docker with or without sudo

Docker user permissions

Using sudo for Docker commands is a valid choice on a rootful Engine installation. The daemon listens on a privileged Unix socket, so a permission error without sudo doesn't mean Docker failed to start.

The Docker Linux post-installation guide also covers the optional docker group. Membership gives root-level privileges through the daemon, so add only accounts you trust with that access. Rootless Docker is a third option. It's a separate setup that runs both the daemon and containers without host-root privileges, and it has additional prerequisites.

Docker socket access

If you chose the group and it doesn't exist yet, create it:

sudo groupadd docker

If the group already exists, skip that command. Add your user and refresh the session:

sudo usermod -aG docker "$USER"
newgrp docker

Logging out and back in also applies the new membership. Then run an ordinary Docker command without sudo. Never make /var/run/docker.sock world-writable to get around access controls. Protect remote daemon access with an authenticated method such as SSH or TLS.

Published ports and UFW

Docker published ports

Docker sets up packet-filtering and routing rules for bridge-network traffic and published container ports. With UFW enabled, a container port can still be reachable, because Docker's rules process traffic before the UFW chains you expect to govern it.

The Docker firewall documentation describes this behavior. A public web service and a private administrative interface need different host bindings.

Container network exposure

Docker shows two publishing forms for a bridge-network container:

-p 8080:80
-p 127.0.0.1:8080:80

The first publishes container port 80 on host port 8080 on all of the host's network addresses by default. The second binds that host port to loopback, which suits an interface that should stay local. Engine releases older than 28.0.0 had a same-L2-segment exception that allowed neighboring hosts to reach localhost-published ports.

A Debian 12 user on r/selfhosted put it this way in January 2025: “However, I was surprised to see that I could access my running Docker containers on my local home network even though I didnt allow those ports in UFW.”

Before launching a real service, pick the bind address and published port, then test reachability from a network that shouldn't have access.

Fix common Docker installation errors

Docker installation errors

Find the failing layer before touching packages or permissions.

Error or symptomFirst checkFix
Conflicting values set for option Signed-ByCompare Docker entries in /etc/apt/sources.list and /etc/apt/sources.list.d/Keep one definition, as described in the repository section
Package 'docker-ce' has no installation candidateapt-cache policy docker-ce shows which version, if any, APT can select from its sources. Also check the Ubuntu codename and the pasted commandFix the source or command error that stops an upstream candidate from appearing
Cannot connect to the Docker daemonCheck the Docker service state and the endpoint the CLI usesFix the inactive daemon or wrong endpoint that check turns up
permission denied on /var/run/docker.sockid -nG and the active session's group membershipUse one of the access methods in the permissions section
docker compose unavailabledpkg -l docker-compose-pluginInstall the missing plugin from your chosen package family
hello-world image download failsTell a registry or image pull error apart from a daemon connection errorCheck network access to the image registry before changing Engine packages

Also compare the command you ran with the one in this guide. Accidental duplication or a broken line break can change what the shell runs.

Docker package diagnosis

Updates and removal

Docker maintenance steps

Docker updates through the package source you chose. Manual .deb packages need manual upgrades. Uninstalling Docker packages doesn't delete /var/lib/docker/ automatically.

NoScript Team