---
title: "How to install Docker and Docker Compose on Ubuntu and Debian"
description: "Docker Engine and Compose from the official repository on an Ubuntu or Debian VPS: running without sudo, a first compose.yaml, the Docker and UFW trap, log limits."
url: https://tihost.io/en/blog/install-docker-ubuntu-debian
language: en
section: "Guides"
published: 2026-09-24
updated: 2026-10-05
publisher: Tihost (https://tihost.io)
---

# How to install Docker and Docker Compose on Ubuntu and Debian

> **In short:** Install Docker from Docker's official repository rather than the docker.io package: that gets you a current Engine and the Compose plugin. Right after installing, cap container log size, and remember that Docker publishes ports around the UFW firewall - expose only what should be reachable from the internet.

**Key takeaways:**

- On Ubuntu 22.04/24.04 and Debian 12, Docker Engine is installed from Docker's official repository as `docker-ce` with `docker-compose-plugin`, not as the distribution's `docker.io` package - that brings a current Engine and the `docker compose` command.
- Docker publishes container ports through its own iptables rules around the UFW firewall: a port published as `8080:80` is reachable from the internet even if UFW blocks it, so internal services should be published only on `127.0.0.1`.
- By default Docker keeps container logs with no size limit; setting `max-size: 10m` and `max-file: 3` in `/etc/docker/daemon.json` caps logs at 30 MB per container.
- A user in the `docker` group effectively has root on the server, so only your own administrator account should be added to that group.
- Docker and Docker Compose run without restrictions on a KVM VPS; Docker itself is fine with 2 GB of RAM, and the containers determine the rest.

> Commands are checked for Ubuntu 22.04/24.04 and Debian 12 on a KVM VPS - Docker runs on it without restrictions. It is worth doing the [first server setup](https://tihost.io/en/blog/vps-first-setup) before installing.

## Step 1. Add the Docker repository and install the packages

The same commands work on Ubuntu and Debian: `/etc/os-release` fills in the distribution name and release. This is the official method from the [Docker documentation](https://docs.docker.com/engine/install/):

```bash
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
sudo curl -fsSL "https://download.docker.com/linux/$ID/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$ID ${UBUNTU_CODENAME:-$VERSION_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

## Step 2. Check the installation

Run the `hello-world` test container and check the Compose version - both commands should finish without errors:

```bash
sudo docker run --rm hello-world
docker compose version
```

If `hello-world` prints its greeting, the Engine works. The Docker service is already enabled at boot and will come back after a server reboot.

## Step 3. Run without sudo

To stop typing `sudo` before every `docker` command, add your user to the `docker` group and log in over SSH again:

```bash
sudo usermod -aG docker $USER
# log out of SSH and back in, then:
docker ps
```

> Membership in the `docker` group is equivalent to root: Docker can mount any server directory into a container. Add only your own user to the group.

## Step 4. A first Docker Compose project

Create a project directory and a `compose.yaml` file - the example runs nginx reachable only from the server itself on port 8080:

`compose.yaml`:

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

```bash
docker compose up -d
docker compose ps
docker compose logs -f
```

`restart: unless-stopped` brings the container back after a crash and after a server reboot - it needs no separate systemd service.

## Docker and UFW: ports open around the firewall

Docker writes its own iptables rules for published ports, and they match before UFW rules do. A port published as `8080:80` is reachable from the internet even if UFW keeps it closed. So:

- publish internal services (databases, admin panels, APIs without auth) only on `127.0.0.1`, as in the example above;
- expose a single reverse proxy (nginx, Caddy, Traefik) on ports 80 and 443 and let it forward requests to containers - the [HTTPS guide](https://tihost.io/en/blog/nginx-letsencrypt-https) shows how to set up nginx with a free certificate;
- do not publish ports at all for containers that need no outside access - within one Compose project they reach each other by service name.

## Step 5. Cap log size

By default Docker keeps container logs with no size limit, and a chatty container can fill the whole disk. Cap them at three 10 MB files per container. The setting applies to containers created after it - recreate existing ones with `docker compose up -d --force-recreate`.

```bash
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
EOF
sudo systemctl restart docker
```

## How to free disk space

Old images pile up after every update. The first command shows what takes the space, the second removes images no container uses:

```bash
docker system df
docker image prune -a
```

**Launch a server in 2 minutes.** AMD Ryzen 9, NVMe and DDoS protection in Germany, Finland and Poland. Pay with crypto or card. [Order a Server](https://tihost.io/login)

## FAQ

### How is the docker.io package different from docker-ce?

`docker.io` is built by the distribution itself and is usually older. `docker-ce` comes from Docker's official repository with a current Engine and the Compose plugin; it is what the Docker documentation recommends.

### Does Docker work on a Tihost VPS?

Yes. Servers run on KVM - a full virtual machine with its own kernel - so Docker, Compose and container networking work without restrictions.

### Why is a container reachable from the internet even though UFW is closed?

Docker publishes ports through its own iptables rules, which match before UFW. Publish internal services on `127.0.0.1` or do not publish them at all.

### What is the difference between docker compose and docker-compose?

`docker-compose` is the old standalone Python program and is no longer developed. `docker compose` (with a space) is a Docker CLI plugin installed by the `docker-compose-plugin` package.

### How much memory does Docker need?

Docker itself is fine with 2 GB of RAM. The rest depends on your containers: several services with a database are more comfortable on 4-8 GB.

---

Updated 2026-10-05 · https://tihost.io/en/blog/install-docker-ubuntu-debian
