# Install Docker on Ubuntu 22.04, 24.04 and 26.04

> Install Docker Engine and Docker Compose on Ubuntu 22.04, 24.04 or 26.04 LTS from the official apt repository, run it without sudo and fix common errors.

Docker runs an application in a container together with everything it depends on. For a VPS this means most self hosted software starts with one command or one `compose.yaml` file, and removing it leaves the system clean. This guide installs Docker Engine, the Buildx plugin and the Compose plugin from Docker's own apt repository. The steps are the same on every supported Ubuntu LTS release, and the commands detect your release automatically.

## Which Ubuntu versions are supported

Docker publishes packages for the following Ubuntu releases (checked against the Docker documentation in November 2026):

| Ubuntu release | Codename | Type | Docker repository |
|---|---|---|---|
| 26.10 | Stonking | Interim, 9 months of support | Supported |
| 26.04 | Resolute | LTS | Supported |
| 24.04 | Noble | LTS | Supported |
| 22.04 | Jammy | LTS | Supported |

On a server, pick an LTS release. **Ubuntu 26.04 LTS is fully supported** by Docker's repository, so there is no reason to wait with it. 24.04 and 22.04 keep working too. Older releases such as 20.04 are no longer in the list.

Docker builds packages for 64 bit systems only: `amd64` (x86_64), `arm64`, `armhf`, `s390x` and `ppc64le`.

Distributions built on Ubuntu, such as Linux Mint, are not officially supported. They report their own codename, which the repository does not know. On Debian, follow [install Docker on Debian](https://datapasa.com/wiki/server-management/install-docker-on-debian) instead, because Debian uses a different repository URL.

## Before you start

- **A VPS with Ubuntu 22.04, 24.04 or 26.04.** Docker Engine itself needs little memory. The containers you run decide the rest. For one or two small apps, **VPS-1** (1 vCPU, 1 GB) is enough. A typical stack of an app, a database and a reverse proxy is more comfortable on **VPS-2** (1 vCPU, 2 GB) or **VPS-3** (2 vCPU, 4 GB). See [how much RAM your VPS needs](https://datapasa.com/wiki/getting-started/how-much-ram-does-my-vps-need) and the [pricing page](https://datapasa.com/en/pricing).
- **SSH access** as root or as a user with sudo rights. See [how to connect via SSH](https://datapasa.com/wiki/server-management/connect-via-ssh). The commands below use `sudo`. As root you can leave it out.

> **Why not the docker.io package?.** Ubuntu ships its own `docker.io` package. It is maintained by Ubuntu, usually lags behind Docker's releases, and Buildx and Compose come as separate Ubuntu packages with their own versions. Docker's repository gets new versions on the day of release and keeps Engine, CLI and plugins in step.

## Step 1. Check your Ubuntu version

Make sure you are on a supported release and a 64 bit architecture:

```bash
lsb_release -ds
. /etc/os-release && echo "$VERSION_CODENAME"
dpkg --print-architecture
```

Example output:

```text
Ubuntu 24.04.3 LTS
noble
amd64
```

The codename (`jammy`, `noble` or `resolute`) is what the repository uses in Step 3. If it prints something that is not in the table above, stop here, because the repository will not have packages for it.

Install pending updates before you add new software:

```bash
sudo apt update && sudo apt upgrade -y
```

## Step 2. Remove conflicting packages

Docker's packages conflict with several unofficial ones: `docker.io`, `docker-compose`, `docker-compose-v2`, `docker-doc`, `docker-buildx`, `podman-docker`, and the standalone `containerd` and `runc` (Docker ships its own `containerd.io`). This command removes whichever of them are installed and does nothing on a clean system:

```bash
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)
```

Images, containers and volumes under `/var/lib/docker` stay where they are.

Also check for Docker installed as a snap. Some Ubuntu installers offer it as an option:

```bash
snap list docker
```

If the snap is listed, it will compete with the apt version for the `docker` command. Move any data you need out of it first, then remove it:

```bash
sudo snap remove --purge docker
```

`--purge` skips the automatic snapshot, so the snap's images and volumes are gone afterwards. Leave the flag out if you want snapd to keep a snapshot.

## Step 3. Add Docker's apt repository

Install the tools needed to fetch the key and save Docker's signing key:

```bash
sudo apt update
sudo apt install -y 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
```

Then add the repository in the deb822 format. The `Suites` line fills in your codename and `Architectures` your CPU architecture:

```bash
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
sudo apt update
```

The last `apt update` should list a line with `download.docker.com` and finish without errors. If it complains about keys or `Signed-By`, jump to [Troubleshooting](#troubleshooting).

## Step 4. Install Docker Engine and Compose

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

| Package | What it is |
|---|---|
| `docker-ce` | Docker Engine, the background daemon |
| `docker-ce-cli` | The `docker` command |
| `containerd.io` | The container runtime the daemon uses |
| `docker-buildx-plugin` | `docker buildx`, the image builder |
| `docker-compose-plugin` | `docker compose`, for multi container projects |

The package starts the daemon and enables `docker.service` and `containerd.service` at boot. Check it:

```bash
sudo systemctl status docker
```

You want to see `active (running)`. Press `q` to leave the status view.

> **Need a specific version?.** List what the repository offers with `apt list --all-versions docker-ce`, then install with `docker-ce=VERSION docker-ce-cli=VERSION` and the other packages as above. Most people should simply take the latest.

## Step 5. Test the installation

```bash
sudo docker run hello-world
```

Docker pulls a tiny test image, starts a container from it and prints `Hello from Docker!` followed by a short explanation of what just happened. The container exits right after. Now check the versions:

```bash
docker --version
docker compose version
```

The Compose command is `docker compose` with a space. The plugin replaces the old standalone `docker-compose` script, which you do not need to install.

## Step 6. Run Docker without sudo

The Docker daemon listens on a Unix socket, `/var/run/docker.sock`, that belongs to root and the `docker` group. To use `docker` as your regular user, add the user to that group:

```bash
sudo groupadd docker
sudo usermod -aG docker $USER
```

The package normally creates the group, so `groupadd` may say it already exists. That is fine. Group membership is read at login, so log out and connect again, or apply it to the current shell:

```bash
newgrp docker
docker run hello-world
```

> **The docker group is root access.** Anyone in the `docker` group can start a container that mounts `/` from the host and change any file. Treat membership like sudo rights. If you need stronger isolation, Docker offers a rootless mode, which is beyond this guide.

## Step 7. Start your first container stack

A quick check that publishing ports and Compose work together. Create a small project:

```bash
mkdir -p ~/hello-nginx && cd ~/hello-nginx
nano compose.yaml
```

```yaml
services:
  web:
    image: nginx:stable
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"
```

```bash
docker compose up -d
curl -I http://127.0.0.1:8080
docker compose down
```

`HTTP/1.1 200 OK` in the `curl` output means everything works. For a real project with a database, an `.env` file and health checks, continue with [Docker Compose on Ubuntu](https://datapasa.com/wiki/server-management/docker-compose-on-ubuntu).

## Docker and the firewall

This catches many people on a new VPS. When a container publishes a port (`-p 8080:80` or `ports: - "8080:80"`), Docker adds its own iptables rules, and traffic reaches the container **even if UFW denies that port**. UFW and firewalld simply do not see these packets.

Three habits keep you safe:

- **Bind internal services to localhost**: `127.0.0.1:8080:80`. They stay reachable for a reverse proxy on the same server or through an SSH tunnel, but not from the internet.
- **Publish publicly only what must be public**, usually ports 80 and 443 of the reverse proxy.
- **Check what listens on all interfaces** after starting something new:

```bash
sudo ss -tlnp
```

Lines with `0.0.0.0:PORT` or `[::]:PORT` are open to the world. If you need real filtering in front of containers, Docker expects the rules in the `DOCKER-USER` iptables chain. For the basic server firewall, see [secure your VPS with UFW and fail2ban](https://datapasa.com/wiki/server-management/secure-vps-ufw-fail2ban).

## Limit the size of container logs

By default Docker keeps container output in JSON files without a size limit. A chatty container can fill the disk in a few weeks. Set a limit for all containers in `/etc/docker/daemon.json`:

```bash
sudo nano /etc/docker/daemon.json
```

```json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
```

```bash
sudo systemctl restart docker
```

The setting applies to containers created after the restart. Existing ones keep their old settings until you recreate them, for example with `docker compose up -d --force-recreate`.

## How to update Docker

Docker installed from the repository updates through apt like every other package. A normal system upgrade includes it:

```bash
sudo apt update && sudo apt upgrade
```

To update only the Docker packages:

```bash
sudo apt update
sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

The upgrade restarts the daemon, so running containers stop for a few seconds. Containers with a restart policy such as `unless-stopped` come back on their own. Plan updates for a quiet moment on production servers. If you have `unattended-upgrades` enabled, note that by default it only installs Ubuntu security updates, not packages from Docker's repository.

Updating Docker does not update your images. That is a separate step per project, covered in [Docker Compose on Ubuntu](https://datapasa.com/wiki/server-management/docker-compose-on-ubuntu#update-images).

## Troubleshooting

### Permission denied on /var/run/docker.sock

```text
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
```

The wording differs slightly between Docker versions, but it always mentions `permission denied` and `docker.sock`. Your user is not in the `docker` group yet, or the current session started before you added it. Check:

```bash
groups
```

If `docker` is missing from the list, run the commands from [Step 6](#step-6-run-docker-without-sudo), then log out and back in. Do not fix this with `chmod 666 /var/run/docker.sock`: it gives every local user root access and resets after a restart anyway.

A related warning, `Error loading config file: /home/USER/.docker/config.json ... permission denied`, appears when you ran `docker` with `sudo` before and root now owns `~/.docker`. Give the folder back to your user:

```bash
sudo chown "$USER":"$USER" "$HOME/.docker" -R
sudo chmod g+rwx "$HOME/.docker" -R
```

### Cannot connect to the Docker daemon

```text
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
```

The client works, but the daemon is not running or failed to start. Start it and read why it stopped:

```bash
sudo systemctl start docker
sudo systemctl status docker
sudo journalctl -u docker --no-pager -n 50
```

Common causes in the log are a syntax error in `/etc/docker/daemon.json` (one missing comma is enough) and a full disk (`df -h /var/lib/docker`). If the `DOCKER_HOST` environment variable is set in your shell, the client looks for the daemon elsewhere. Check it with `echo $DOCKER_HOST` and `unset DOCKER_HOST` if it should not be there.

### Docker from snap and apt at the same time

Symptoms: containers you started disappear, `docker ps` shows different results with and without sudo, or paths like `/var/snap/docker` show up in errors. Check which binary runs:

```bash
which -a docker
snap list docker
```

Only `/usr/bin/docker` should be listed. If `/snap/bin/docker` appears, remove the snap as shown in [Step 2](#step-2-remove-conflicting-packages), then run `hash -r` so the shell forgets the old path.

### NO_PUBKEY or "is not signed" during apt update

```text
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY ...
```

A similar message is `The repository ... is not signed`. In both cases apt cannot use the key for Docker's repository. Usually the key download failed or the file is not readable. Fetch it again and check the permissions:

```bash
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
ls -l /etc/apt/keyrings/docker.asc
sudo apt update
```

### Conflicting values set for option Signed-By

```text
E: Conflicting values set for option Signed-By regarding source https://download.docker.com/linux/ubuntu/ noble: ...
```

The repository is defined twice with different keys. This happens on servers that followed an older guide, which created `/etc/apt/sources.list.d/docker.list`. Keep the new `docker.sources` file and delete the old one:

```bash
ls /etc/apt/sources.list.d/ | grep -i docker
sudo rm /etc/apt/sources.list.d/docker.list
sudo apt update
```

### Unable to locate package docker-ce

```text
E: Unable to locate package docker-ce
E: Package 'docker-ce' has no installation candidate
```

apt does not see Docker's repository. Either Step 3 was skipped, `apt update` was not run after it, or the codename in the repository file is wrong. Look at the file:

```bash
cat /etc/apt/sources.list.d/docker.sources
```

The `Suites:` line must be `jammy`, `noble`, `resolute` or another release from the table at the top. An empty value or the codename of a derivative distribution leads to errors like `does not have a Release file`.

### docker: 'compose' is not a docker command

```text
docker: 'compose' is not a docker command.
```

The Compose plugin is missing. With Docker's repository set up, install it:

```bash
sudo apt install docker-compose-plugin
```

The opposite case is `docker-compose: command not found`. That is the old Compose v1 command. You do not need it: write `docker compose` instead. Migration from v1, and the case where Docker came from Ubuntu's `docker.io` package, are covered in [Docker Compose on Ubuntu](https://datapasa.com/wiki/server-management/docker-compose-on-ubuntu).

## Everyday commands

| Task | Command |
|---|---|
| List running containers | `docker ps` |
| List all containers, including stopped | `docker ps -a` |
| Follow a container's logs | `docker logs -f NAME` |
| Open a shell in a container | `docker exec -it NAME sh` |
| Disk usage of images, containers and volumes | `docker system df` |
| Remove unused images | `docker image prune -a` |

For Compose projects there is a separate [Docker Compose commands cheat sheet](https://datapasa.com/wiki/server-management/docker-compose-commands).

Data that should survive a container belongs in volumes or bind mounts, and those need backups like any other data. See [how to back up your VPS](https://datapasa.com/wiki/server-management/back-up-your-vps).

## FAQ

### Is Docker free on Ubuntu?

Yes. Docker Engine, the CLI and the Compose and Buildx plugins are open source and free to use on servers, including commercial use. The paid subscriptions are for Docker Desktop and Docker Hub features, which a Linux server does not need.

### Should I use the official script from get.docker.com?

The convenience script at `get.docker.com` sets up the same repository and installs the same packages in one go. It is handy for throwaway test machines. On a server you keep, the manual steps above are easier to understand and to repeat. Either way, later updates come through apt. Do not run the script again to upgrade.

### Does Docker work on Ubuntu 26.04 LTS?

Yes. Ubuntu 26.04 "Resolute" is on Docker's list of supported releases, and the commands in this guide work on it without changes.

### How do I check that Docker starts after a reboot?

```bash
systemctl is-enabled docker
```

It should print `enabled`. Containers come back only if they have a restart policy, for example `restart: unless-stopped` in Compose or `--restart unless-stopped` with `docker run`.

### How much RAM does Docker need?

Docker itself needs very little. What counts is the sum of your containers. A small web app may need 50 to 200 MB, a database a few hundred MB more. If memory gets tight, check `docker stats` to see which container uses what.

### How do I remove Docker completely?

Removing the packages, images, volumes and repository is described step by step in [how to uninstall Docker](https://datapasa.com/wiki/server-management/uninstall-docker).

### Is the docker group a security risk?

It is equal to root access on that server. That is acceptable for your own admin user, but do not add service accounts or other people you would not give sudo to.

## Summary

Docker Engine, Buildx and Compose are installed from Docker's repository and update with `apt` like the rest of the system. You can run containers without sudo, keep published ports from bypassing the firewall and cap log growth. Next steps: build a real multi container project in [Docker Compose on Ubuntu](https://datapasa.com/wiki/server-management/docker-compose-on-ubuntu), or deploy something useful right away, such as [Uptime Kuma](https://datapasa.com/wiki/server-management/monitor-with-uptime-kuma), [n8n](https://datapasa.com/wiki/guides/install-n8n-on-vps), a [TeamSpeak 6 voice server](https://datapasa.com/wiki/guides/teamspeak-6-server-docker) or the [Coolify](https://datapasa.com/wiki/guides/install-coolify-on-vps) platform.
