Appearance
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.ascAdd 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 updateStep 3. Install Docker Engine and Compose
bash
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginThe service starts automatically. Check it:
bash
sudo systemctl status dockerLook 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-worldDocker downloads a tiny image, runs it and prints Hello from Docker! with a short explanation. Then check the Compose plugin:
bash
docker compose versionNote 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 $USERgroupadd 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-worldThe 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.yamlyaml
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:8080You 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 -tlnpAddresses like 0.0.0.0:8080 or [::]:8080 are reachable from the internet.
Everyday commands
| Task | Command |
|---|---|
| List running containers | docker ps |
| Show logs of a Compose project | docker compose logs -f |
| Update images and restart | docker compose pull && docker compose up -d |
| Disk usage by images and volumes | docker system df |
| Remove unused images | docker 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.