# Install Docker

> Install Docker Engine and the Compose plugin from Docker's official apt repository on Ubuntu 24.04 or Debian 12, and keep ports private.

Source: https://vpsnine.com/help/install-docker · Updated: 2026-10-09

VPSNine servers are KVM, so Docker runs just as it would on a physical machine. We'll install Docker Engine and the Compose plugin from Docker's own apt repository, which is newer than the `docker.io` package your distribution ships. Then we'll keep your containers' ports off the public internet, which Docker doesn't do for you.

## Clear out old packages

If anything Docker-related came from the distribution, remove it. On a fresh server this does nothing, and apt just skips what isn't there.

```bash
apt remove docker.io docker-doc docker-compose podman-docker containerd runc
```

On Ubuntu, remove the distribution's Compose package too:

```bash
apt remove docker-compose-v2
```

## Add Docker's repository

The commands are nearly identical for the two distributions, but the URL differs, so use the right block.

**Ubuntu 24.04:**

```bash
apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
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/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
```

**Debian 12:**

```bash
apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
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/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
```

That long last line fills in `noble` or `bookworm` for you, so you don't have to.

## Install it

Same on both:

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

Check it:

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

You should get `Hello from Docker!` and a Compose version number. Docker starts at boot on its own; `systemctl is-enabled docker` says `enabled`.

## Keep published ports private

Docker writes its own firewall rules, and a port published with `-p 8080:80` is reachable from the whole internet **even if ufw doesn't allow it**. `ufw status` won't mention it either. People find this out the hard way, usually with an open database.

If a container only needs to be reached from the server itself (say, behind nginx), bind it to localhost:

```bash
docker run -d -p 127.0.0.1:8080:80 nginx
```

In a Compose file:

```yaml
services:
  app:
    image: nginx
    ports:
      - "127.0.0.1:8080:80"
```

Then put a reverse proxy in front. [Serve a site with nginx and HTTPS](https://vpsnine.com/help/nginx-and-https) covers the HTTPS side.

## Docker without root

To let a normal user run `docker`, add them to the `docker` group and have them log in again (replace `deploy`):

```bash
usermod -aG docker deploy
```

Be clear about what that means: anyone in the `docker` group can get root on the server. Only add people you'd give root to anyway.

## When it goes wrong

- `Unable to locate package docker-ce`: the repository line is off. Check that `/etc/apt/sources.list.d/docker.list` says `ubuntu` or `debian` to match your system, with the right codename, then `apt update` again.
- `NO_PUBKEY` during `apt update`: the key file is missing or unreadable. Repeat the `curl` and `chmod a+r` steps.
- `Cannot connect to the Docker daemon`: start it with `systemctl start docker`, and read `journalctl -u docker` to find out why it stopped.
