Skip to content

How to install Docker and Docker Compose on Ubuntu

Docker packages an application together with everything it needs into a container, so most self hosted software can be started with one command or one compose.yaml file. This guide installs Docker Engine and the Compose plugin on Ubuntu 24.04 from Docker's official apt repository, following the steps in the Docker documentation.

Before you start

  • A VPS with Ubuntu 24.04 or 22.04, 64 bit. Docker itself needs little, but containers add up, so see how much RAM your VPS needs.
  • SSH access as root or as a sudo user. The commands below use sudo. If you are logged in as root, you can drop it.

Why not the docker.io package?

Ubuntu's own repositories contain a docker.io package, but it is maintained by Ubuntu and often lags behind. Docker's repository gets new versions first and includes the Compose and Buildx plugins.

Step 1. Remove conflicting packages

If anything Docker related was installed before, remove it. On a fresh server apt simply reports that none of these packages are installed:

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 and volumes in /var/lib/docker are not touched by this command.

Step 2. Add Docker's apt repository

Install the prerequisites and 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

Add the repository. The command detects your Ubuntu release and CPU architecture automatically:

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

Step 3. Install Docker Engine and Compose

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

The service starts automatically. Check it:

bash
sudo systemctl status docker

Look for active (running). Docker and containerd are also enabled at boot, so containers with a restart policy come back after a reboot.

Step 4. Test the installation

Run the official test image:

bash
sudo docker run hello-world

Docker downloads a tiny image, runs it and prints Hello from Docker! with a short explanation. Then check the Compose plugin:

bash
docker compose version

Note the command is docker compose with a space. The old standalone docker-compose binary is not needed.

Step 5. Use Docker without sudo

By default only root can talk to the Docker daemon. To run docker as your regular user, add it to the docker group:

bash
sudo groupadd docker
sudo usermod -aG docker $USER

groupadd may report that the group already exists, which is fine. Log out and back in, or run newgrp docker to apply the change in the current session. Test:

bash
docker run hello-world

The docker group equals root

Members of the docker group can start a container that mounts the whole filesystem, so the group gives root level access to the server. Add only users you would trust with root.

Step 6. Run a first Compose project

Compose describes one or more containers in a YAML file. Create a folder and a file:

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"

Start it in the background and check it:

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

You should see HTTP/1.1 200 OK. Stop and remove it with docker compose down.

Publish ports safely

This is the most common surprise on a VPS. When you publish a port with -p 8080:80 or ports: - "8080:80", Docker writes its own firewall rules, and traffic reaches the container even if UFW blocks that port. The Docker documentation warns about this explicitly.

Two simple habits avoid it:

  • Bind to localhost when a service is only used through a reverse proxy or an SSH tunnel: 127.0.0.1:8080:80, as in the example above.
  • Publish publicly only what must be public, such as ports 80 and 443 of your reverse proxy.

Check what is listening on all interfaces with:

bash
sudo ss -tlnp

Addresses like 0.0.0.0:8080 or [::]:8080 are reachable from the internet.

Everyday commands

TaskCommand
List running containersdocker ps
Show logs of a Compose projectdocker compose logs -f
Update images and restartdocker compose pull && docker compose up -d
Disk usage by images and volumesdocker system df
Remove unused imagesdocker image prune

Container data you want to keep belongs in volumes or bind mounts, and those need backups like any other data. See how to back up your VPS.

Summary

Docker Engine and Compose are installed from the official repository and will update together with the rest of the system through apt. You can run containers as your user and you know how to keep published ports from bypassing the firewall. A good first project is Uptime Kuma for monitoring.