Install Docker on Ubuntu: APT, Snap, Rootless — Complete Guide 2026
Pick the right Docker install path on Ubuntu.
Installing Docker on Ubuntu should be simple, but in practice several Docker-shaped options compete for the same command name, each with different packaging, upgrade behavior, and security implications.
This guide compares every major install path so you can pick the one that fits your machine.
The options you will encounter include:
docker.iofrom Ubuntu repositoriesdocker-cefrom Docker’s official APT repository- Docker from Snap
- Docker Desktop
- manually downloaded
.debpackages - the Docker convenience script
- rootless Docker

Although they all provide container tooling, they are not interchangeable packages. The best choice depends on whether the machine is a developer workstation, a CI runner, a small server, a self-hosting box, or a production host. My default recommendation is calm but firm: for most technical users on normal Ubuntu machines, install Docker Engine from Docker’s official APT repository. Use Ubuntu’s docker.io only when distribution integration matters more than upstream Docker packaging. Avoid the Snap package unless you specifically want Snap behavior and understand its limits. Rootless Docker is worth knowing about, but it is not automatically the best default for every machine.
This guide explains the tradeoffs, covers post-install security, and gives you clean installation paths for each method. Once Docker Engine is running, the Docker Cheatsheet is your daily command reference, and the Docker Compose Cheatsheet covers multi-container setups. Both sit alongside Git, VS Code, and CI/CD guides in Developer Tools: The Complete Guide to Modern Development Workflows.
Quick Recommendation
The table below summarizes which install path fits common scenarios.
| Use case | Recommended install |
|---|---|
| Developer workstation | Docker official APT repo |
| CI runner | Docker official APT repo, version pinned if needed |
| Small self-hosted server | Docker official APT repo |
| Production server | Docker official APT repo, controlled upgrades |
| Ubuntu-only conservative system | Ubuntu docker.io package |
| Quick desktop experiment | Docker Desktop or official APT repo |
| Snap-managed Ubuntu setup | Docker Snap, with caution |
| Strong non-root daemon requirement | Rootless Docker |
| Air-gapped host | Manual .deb packages or internal mirror |
If you do not have a special reason to choose otherwise, Docker’s official APT repository is the default.
What Gets Installed
A normal Docker Engine setup includes several moving parts:
- Docker daemon:
dockerd - Docker CLI:
docker - container runtime:
containerd - low-level runtime:
runc - Buildx plugin:
docker buildx - Compose plugin:
docker compose
Modern Docker Compose is usually installed as a Docker CLI plugin. That means the command is:
docker compose version
Not:
docker-compose version
The old docker-compose command still exists in older guides and older systems, but new Ubuntu setups should generally use the Compose plugin.
Option 1: Install Docker from Docker’s Official APT Repository
This is the best default for most developers and DevOps users. You get Docker’s upstream packaging, current Docker Engine releases, Buildx, Compose plugin, and a normal APT upgrade path.
Remove Conflicting Packages First
Before installing Docker CE, remove packages that may conflict with the official Docker packages.
sudo apt remove docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
It is fine if APT says some of these packages are not installed.
This command does not remove Docker images, containers, volumes, or networks stored under /var/lib/docker. If you want a clean reset, that is a separate step and should be done deliberately.
Add Docker’s Official APT Repository
Install prerequisites:
sudo apt update
sudo apt install ca-certificates curl
Create the keyring directory:
sudo install -m 0755 -d /etc/apt/keyrings
Download Docker’s repository key:
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
Allow APT to read the key:
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the Docker repository using the deb822 .sources format:
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<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
Update APT metadata:
sudo apt update
Install Docker Engine, Buildx, and Compose
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Check the service:
sudo systemctl status docker
If it is not running:
sudo systemctl start docker
Verify the install:
sudo docker run hello-world
Check versions:
docker --version
docker buildx version
docker compose version
At this point Docker works, though you still need sudo for most commands unless you configure non-root access in the post-install section below.
Option 2: Install Docker from Ubuntu Repositories
Ubuntu provides the docker.io package, which you can install with:
sudo apt update
sudo apt install docker.io docker-compose-v2
Start and enable Docker:
sudo systemctl enable --now docker
Verify:
sudo docker run hello-world
When Ubuntu docker.io Makes Sense
Ubuntu’s package can be a good choice when:
- You prefer Ubuntu-maintained packages.
- You want a version aligned with Ubuntu’s release process.
- You are managing many Ubuntu hosts with standard repositories.
- You do not need the newest upstream Docker release.
- You want fewer third-party APT sources.
This is a reasonable choice. It is not “wrong.”
When Ubuntu docker.io Is Not Ideal
Use Docker’s official repository instead when:
- You want the current upstream Docker Engine.
- You follow Docker’s own documentation.
- You rely on current Buildx and Compose behavior.
- You want Docker CE package names.
- You are debugging issues against upstream Docker docs.
- You need predictable Docker-version parity across distributions.
My bias: for developer machines and container-heavy hosts, use Docker’s official APT repository. For conservative Ubuntu-managed machines, docker.io is acceptable.
Option 3: Install Docker with Snap
The Docker snap installs with a single command, but simplicity does not always mean predictable behavior on a server or development machine.
sudo snap install docker
Snap packages have their own packaging model, update behavior, confinement assumptions, and filesystem layout. That is fine for many desktop apps, but Docker Engine is already a system-level container runtime, so the extra Snap layer can surprise people. If you manage other software with Snap, the Snap Package Manager Cheatsheet explains channels, confinement, and update behavior in more detail.
When Docker Snap Makes Sense
Docker Snap may be reasonable when:
- You intentionally manage software with Snap.
- You are using Ubuntu Core or a Snap-heavy environment.
- You want snap-style automatic updates.
- You are experimenting and do not care about matching Docker’s upstream APT instructions.
Why I Usually Avoid Docker Snap
I usually avoid the Docker snap for development and server use because:
- Most Docker documentation assumes the standard Docker Engine layout.
- Troubleshooting paths can differ from APT installs.
- Service management may feel less transparent.
- Snap auto-updates can be inconvenient for infrastructure software.
- Some bind mounts, sockets, and host integration details may surprise you.
If Docker is central to your workflow, install it like infrastructure rather than a casual desktop app — even when a Snap install looks tempting on the surface.
Option 4: Install Docker from Manual .deb Packages
Manual .deb installation is useful when:
- The machine cannot use external APT repositories.
- You are building an offline install process.
- You mirror packages internally.
- You need strict change control.
The tradeoff is maintenance, because you must download and install new packages manually whenever you upgrade.
A manual install usually requires these packages:
containerd.iodocker-cedocker-ce-clidocker-buildx-plugindocker-compose-plugin
Install them with:
sudo dpkg -i ./containerd.io_*.deb \
./docker-ce_*.deb \
./docker-ce-cli_*.deb \
./docker-buildx-plugin_*.deb \
./docker-compose-plugin_*.deb
Then fix missing dependencies if needed:
sudo apt --fix-broken install
Verify:
sudo systemctl status docker
sudo docker run hello-world
Manual .deb installs are not my first choice, but they remain valid for controlled or air-gapped environments where explicit package approval matters more than convenience.
Option 5: Use Docker’s Convenience Script
Docker provides a convenience script:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
You can preview what it would do:
sudo sh get-docker.sh --dry-run
The convenience script is useful for disposable test machines, demos, labs, and throwaway environments, but I would not use it as the main install method for production systems. A script that configures repositories and installs packages non-interactively is convenient, yet long-lived infrastructure deserves explicit, reviewable steps — so for production hosts, use the APT repository method directly.
Docker Desktop vs Docker Engine on Ubuntu
Docker Desktop for Linux is a different product from Docker Engine. Docker Engine is the server-side runtime and CLI workflow most Linux server users expect, while Docker Desktop adds a GUI, Desktop integrations, and a product experience closer to macOS and Windows Docker usage.
Use Docker Desktop when:
- You want a graphical Docker experience.
- You want Desktop features.
- You are aligning with a team that standardizes on Docker Desktop.
- You do not mind the extra layer.
Use Docker Engine when:
- You are running a server.
- You want a simple Linux-native runtime.
- You prefer systemd-managed services.
- You are building CI, DevOps, or self-hosted infrastructure.
- You do not need the GUI.
For an advanced technical blog audience, Docker Engine is usually the more interesting default on Linux servers and CI hosts.
Post-Install: Run Docker Without Sudo
After installation, this works:
sudo docker ps
But this may fail:
docker ps
That is because the Docker daemon listens on a Unix socket owned by root. The common fix is to add your user to the docker group.
Create the group if needed:
sudo groupadd docker
Add your user:
sudo usermod -aG docker $USER
Apply the new group membership:
newgrp docker
Or log out and log back in.
Test:
docker run hello-world
Important Security Note About the Docker Group
The docker group is not a harmless convenience group. A user who can control the Docker daemon can usually get root-equivalent control of the host, so for a personal developer machine this is often acceptable, but on a shared server it is a serious access-control decision. Treat membership in the docker group like admin access, and if that feels too broad, consider rootless Docker instead.
Rootless Docker on Ubuntu
Rootless Docker runs the Docker daemon and containers as a non-root user. This is not the same as adding your user to the docker group — with the docker group, the daemon still runs as root, whereas in rootless mode the daemon itself runs as your user.
When Rootless Docker Makes Sense
Rootless Docker is useful when:
- You want to reduce daemon-level root risk.
- You are on a shared development machine.
- You run user-owned containers.
- You do not need every advanced networking and storage feature.
- You want a safer default for experimental workloads.
When Rootless Docker May Be Annoying
Rootless Docker can be less convenient when:
- You need privileged containers.
- You rely on low port binding without extra setup.
- You need some host networking patterns.
- You expect behavior identical to rootful Docker.
- You are following guides written for normal Docker Engine installs.
Rootless mode improves security posture, but it is not zero friction compared with a standard rootful install.
Install Rootless Docker
Install prerequisites:
sudo apt update
sudo apt install uidmap
If you installed Docker from DEB or APT packages, the rootless setup tool should be available:
dockerd-rootless-setuptool.sh install
If rootful Docker is already running and you want only rootless Docker, disable the system daemon:
sudo systemctl disable --now docker.service docker.socket
You may also need to remove the rootful socket:
sudo rm -f /var/run/docker.sock
After installing rootless Docker, the setup tool usually prints environment variables to add to your shell profile. They commonly look like this:
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock
Your UID may differ. Check the exact output from the setup tool.
Enable lingering if you want the user service to run after logout:
sudo loginctl enable-linger $USER
Check the user service:
systemctl --user status docker
Run a test container:
docker run hello-world
Rootful Docker vs Rootless Docker
| Topic | Rootful Docker | Rootless Docker |
|---|---|---|
| Daemon user | root | normal user |
| Command convenience | high | medium |
| Compatibility | highest | good, but not perfect |
| Security posture | weaker by default | better by default |
| Privileged containers | supported | limited |
| Low ports | simple | needs extra setup |
| Server usage | common | possible, but plan carefully |
| Developer workstation | common | good for security-conscious users |
My practical recommendation:
- Use normal rootful Docker for a personal dev machine or simple server.
- Add only trusted users to the
dockergroup. - Use rootless Docker when user isolation matters.
- Do not pretend the
dockergroup is a security boundary.
Enable Docker at Boot
On Ubuntu, Docker installed from normal packages usually starts automatically, but it is worth confirming after a fresh install or migration.
Check:
systemctl is-enabled docker
systemctl is-enabled containerd
Enable manually if needed:
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
Start now:
sudo systemctl start docker
Disable auto-start:
sudo systemctl disable docker.service
sudo systemctl disable containerd.service
For rootless Docker, use the user service:
systemctl --user enable docker
systemctl --user start docker
And enable lingering if needed:
sudo loginctl enable-linger $USER
Install or Verify Docker Compose
With Docker’s official APT repository, install Compose as a plugin:
sudo apt update
sudo apt install docker-compose-plugin
Verify:
docker compose version
If you installed Ubuntu’s package, you may use:
sudo apt install docker-compose-v2
Prefer:
docker compose up -d
Over the old style:
docker-compose up -d
The hyphenated docker-compose command belongs to the older standalone Compose era. Many systems still have it, but new documentation should use docker compose.
Check Which Docker You Have Installed
When you inherit a machine or debug a broken setup, these commands help identify which Docker packaging is actually in use.
Check the Docker binary:
which docker
Check package ownership:
dpkg -S "$(which docker)"
List Docker-related packages:
dpkg -l | grep -E 'docker|containerd|runc'
Check APT policy:
apt-cache policy docker-ce docker.io containerd.io docker-compose-plugin docker-compose-v2
Check Snap:
snap list | grep docker
Check service status:
systemctl status docker
systemctl status containerd
Check Docker server details:
docker info
If docker info fails without sudo, check your group membership:
groups
Migrate from Ubuntu docker.io to Docker CE
Stop Docker:
sudo systemctl stop docker
Remove Ubuntu packages:
sudo apt remove docker.io docker-compose docker-compose-v2 docker-doc containerd runc
Add Docker’s official APT repository using the steps above.
Install Docker CE:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Start Docker:
sudo systemctl start docker
Check existing containers:
docker ps -a
docker images
docker volume ls
Normally, removing packages does not delete /var/lib/docker, so your images, containers, and volumes may still exist. Still, back up important data first.
Migrate from Docker Snap to Docker CE
First inspect what exists:
snap list | grep docker
docker info
Stop workloads and back up important volumes or bind-mounted data.
Remove the snap:
sudo snap remove docker
Then install Docker CE from the official APT repository.
Be careful with data locations. Snap packages often use different paths and confinement rules. Do not assume that Docker Snap data will appear automatically under the standard /var/lib/docker path after migration.
For important services, export or back up data explicitly before switching package sources.
Pin a Docker Version with APT
For production-like systems, you may want controlled upgrades.
List available versions:
apt list --all-versions docker-ce
Install a specific version:
VERSION_STRING="5:29.0.0-1~ubuntu.24.04~noble"
sudo apt install docker-ce=$VERSION_STRING \
docker-ce-cli=$VERSION_STRING \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
Hold Docker packages:
sudo apt-mark hold docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Remove the hold later:
sudo apt-mark unhold docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Do this when you need predictable change windows. For a personal laptop, it may be unnecessary.
Firewall and Networking Notes
Docker modifies packet filtering rules to make container networking work, which matters if you use UFW, firewalld, nftables, or custom firewall rules.
Important points:
- Published Docker ports may bypass naive UFW expectations.
- Docker uses iptables integration.
- Custom rules should account for Docker-created chains.
- Server hardening should be tested with real container port mappings.
- Do not assume
ufw denyprotects a port published by Docker.
Test exposed ports from another machine, not only from localhost.
Example:
docker run --rm -p 8080:80 nginx
Then from another host:
curl http://server-ip:8080
On servers, Docker networking is part of your security model, not an implementation detail you can ignore after install.
Common Errors and Fixes
Permission Denied on Docker Socket
Error:
permission denied while trying to connect to the Docker daemon socket
Fix options:
Use sudo:
sudo docker ps
Or add your user to the docker group:
sudo usermod -aG docker $USER
newgrp docker
Remember that the docker group is root-equivalent in practice, so treat group membership as a privileged access decision.
Docker Daemon Is Not Running
Check status:
sudo systemctl status docker
Start it:
sudo systemctl start docker
Check logs:
journalctl -u docker --no-pager -n 100
Conflicting Docker Packages
If installing Docker CE fails because of conflicts, remove old packages. If APT itself is in a bad state after adding the Docker repository, work through Ubuntu APT troubleshooting for broken packages and GPG errors before retrying.
sudo apt remove docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
Then retry:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Compose Command Not Found
Check modern Compose:
docker compose version
Install plugin:
sudo apt install docker-compose-plugin
If you expected the old command:
docker-compose version
You may be following old documentation. Prefer updating the command to docker compose.
Old Root-Owned Docker Config
If you ran Docker with sudo before setting up group access, your user config may be owned by root.
Fix ownership:
sudo chown "$USER":"$USER" "$HOME/.docker" -R
sudo chmod g+rwx "$HOME/.docker" -R
Or remove the config if you do not need it:
sudo rm -rf "$HOME/.docker"
It will be recreated.
Cannot Connect to Rootless Docker
Check environment:
echo "$DOCKER_HOST"
Check user service:
systemctl --user status docker
Set the socket path if needed:
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
Add it to your shell profile only after confirming it is correct.
Uninstall Docker Engine
Remove Docker CE packages:
sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
Remove Docker data only if you really want to delete images, containers, and volumes:
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
Remove Docker repository files:
sudo rm -f /etc/apt/sources.list.d/docker.sources
sudo rm -f /etc/apt/keyrings/docker.asc
Refresh APT:
sudo apt update
For Docker Snap:
sudo snap remove docker
For Ubuntu docker.io:
sudo apt purge docker.io docker-compose-v2
Recommended Install Path for Most Ubuntu Users
For most technical Ubuntu users, this is the clean path:
sudo apt remove docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
sudo apt update
sudo apt install ca-certificates curl
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
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<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
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker run hello-world
Then decide whether you want group access for convenience or rootless Docker for tighter isolation:
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world
Final Opinionated Guidance
Docker installation on Ubuntu is an operational choice, not just a package choice. The method you pick affects upgrades, security boundaries, and how closely your host matches upstream Docker documentation.
My practical rules are:
- Use Docker’s official APT repository for most developer and DevOps machines.
- Use Ubuntu
docker.iowhen Ubuntu repository consistency matters more than upstream freshness. - Avoid Docker Snap for serious container workflows unless you intentionally want Snap behavior.
- Avoid the convenience script for production hosts.
- Use modern
docker compose, not olddocker-compose. - Treat the
dockergroup as privileged access. - Consider rootless Docker when user isolation matters.
- Pin versions on production-like systems.
- Test firewall behavior when publishing ports on servers.
The most boring Docker install is usually the best one: official APT repo, explicit keyring, Compose plugin, systemd service, understood permissions, and no mystery packaging layer in the middle. Once the engine is in place, multi-service workloads — including self-hosted LLM stacks — are a natural next step; for example, see Ollama in Docker Compose for running local models in containers.
Read More
- https://docs.docker.com/engine/install/ubuntu/ “Install Docker Engine on Ubuntu | Docker Docs”
- https://packages.ubuntu.com/noble/docker.io?utm_source=chatgpt.com “Ubuntu - Details of package docker.io in noble”