# How to install Docker on Debian 12 and Debian 13

> Install Docker Engine and the Compose plugin on Debian 12 or 13 from Docker's apt repository, with sudo setup, the docker group, firewall pitfalls and fixes.

Debian is a popular base for a container host: it is small, stable and changes slowly. This guide sets up Docker Engine and the Docker Compose plugin on a Debian VPS using the apt repository that Docker itself maintains. The same commands work on Debian 13 (trixie) and Debian 12 (bookworm), because the repository line picks the right release on its own.

If your server runs Ubuntu instead, follow [the Ubuntu version of this guide](https://datapasa.com/wiki/server-management/install-docker-on-ubuntu).

## Supported Debian releases

At the time of writing, Docker publishes packages for two Debian releases:

| Debian release | Codename | Status in Debian |
|---|---|---|
| Debian 13 | trixie | stable |
| Debian 12 | bookworm | oldstable |

Older releases such as Debian 11 (bullseye) no longer get new Docker packages. If you are setting up a new server, pick Debian 13. Supported CPU architectures are amd64, arm64, armhf and ppc64el, so a typical KVM VPS is covered. Run `dpkg --print-architecture` if you want to be sure.

Check which release you have:

```bash
cat /etc/os-release
```

The `VERSION_CODENAME` line should say `trixie` or `bookworm`.

## Before you start

- **A Debian 12 or 13 server.** Containers use memory quickly, so read [how much RAM your VPS needs](https://datapasa.com/wiki/getting-started/how-much-ram-does-my-vps-need) before you choose a plan. DataPasa servers run on NVMe storage in Falkenstein, Germany, are billed by the hour from a prepaid balance and start at $1.81 per month on the [pricing page](https://datapasa.com/en/pricing).
- **SSH access.** If you have not logged in yet, see [how to connect via SSH](https://datapasa.com/wiki/server-management/connect-via-ssh).

### Is sudo installed?

This is the first thing that trips people up on Debian. The Debian installer only adds `sudo` when no root password was set, so a fresh Debian VPS where you log in as root often has no `sudo` at all. Running `sudo` then prints `sudo: command not found`.

You have two options.

**Option 1: keep working as root.** Every command in this guide works the same way if you leave out the word `sudo`. This is fine for a quick setup, but a mistyped command as root can do a lot of damage.

**Option 2: install sudo and create a regular user.** As root, run:

```bash
apt update
apt install -y sudo
adduser deploy
usermod -aG sudo deploy
```

Replace `deploy` with any name you like. Log out, connect again as the new user and check that `sudo whoami` prints `root`. Members of the `sudo` group on Debian may run any command with `sudo` after typing their own password. While you are at it, consider switching to key based login with [the SSH keys guide](https://datapasa.com/wiki/server-management/set-up-ssh-keys).

## Docker CE or the docker.io package?

Debian ships its own Docker build in the `docker.io` package. It is packaged by Debian volunteers and follows Debian's release cycle, so on a stable release it stays on the same major version for years and only gets security fixes. Docker CE from `download.docker.com` is built by Docker, gets every new release soon after it comes out, and comes with the current Compose and Buildx plugins.

For a server you will run for a while, Docker CE is the better choice, and the rest of this guide uses it. The two cannot be installed side by side: they ship conflicting files.

## Step 1. Remove conflicting packages

On a clean server nothing needs removing, but run the check anyway in case a hosting image or an earlier experiment left something behind:

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

`dpkg --get-selections` only lists the packages that are really installed, so `apt` removes those and ignores the rest. If none of them are present, apt reports that and stops. Your existing images, containers and volumes under `/var/lib/docker` are left in place. If you want to wipe them too, see [how to uninstall Docker completely](https://datapasa.com/wiki/server-management/uninstall-docker).

## Step 2. Add Docker's repository

First install the two tools needed to fetch and trust the repository, then save Docker's signing key in `/etc/apt/keyrings`:

```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/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
```

Now describe the repository in a `.sources` file. This is the deb822 format that Debian 12 and 13 understand natively, and it is what Docker's own instructions use today instead of the older one line `docker.list` file:

```bash
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
```

The shell fills in `Suites` with your codename (`trixie` or `bookworm`) and `Architectures` with your CPU architecture, usually `amd64`. You can confirm the result with `cat /etc/apt/sources.list.d/docker.sources`. After `apt update` you should see lines mentioning `download.docker.com` without errors.

> **Debian testing and derivatives.** On Debian testing, or on a distribution built from Debian that reports its own codename, the generated `Suites` value will not exist on Docker's server. Edit the file and put the matching stable Debian codename there, for example `trixie`.

## 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
```

What each package does:

- `docker-ce` is the Docker daemon (`dockerd`).
- `docker-ce-cli` is the `docker` command you type.
- `containerd.io` is the container runtime, bundled with `runc`.
- `docker-buildx-plugin` adds `docker buildx` for building images.
- `docker-compose-plugin` adds `docker compose`.

On Debian the service is started and enabled at boot right after installation. Confirm it:

```bash
sudo systemctl status docker
```

You should see `active (running)`. Press `q` to leave the status view. If the service is stopped for some reason, start it with `sudo systemctl start docker`.

## Step 4. Run hello-world

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

Docker cannot find the image locally, pulls it from Docker Hub, starts a container and prints a message that begins with `Hello from Docker!`. The container exits on its own. Next, check the versions:

```bash
sudo docker version
docker compose version
```

Compose v2 is called as `docker compose`, with a space. You do not need the old `docker-compose` program. For a walk through of a real Compose project, see [Docker Compose on Ubuntu](https://datapasa.com/wiki/server-management/docker-compose-on-ubuntu) (the steps are the same on Debian), and keep [the Compose command reference](https://datapasa.com/wiki/server-management/docker-compose-commands) at hand.

## Step 5. Run docker without sudo

The Docker socket belongs to root and to the `docker` group. The package creates the group but puts nobody in it. To use `docker` from your regular account, add yourself:

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

If you get `group 'docker' does not exist`, create it first with `sudo groupadd docker` and repeat the command. Group membership is read at login, so either reconnect over SSH or start a shell with the new group right away:

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

> **Membership in the docker group is root access.** Anyone in the `docker` group can start a container that mounts `/` from the host and change any file on it. Treat adding a user to this group exactly like giving them the root password. On a server shared with other people, keep the group empty and use `sudo docker` instead.

## Firewalls on Debian: what to know

### Published ports skip UFW

Debian does not ship with a firewall front end enabled, so many people install UFW following [our hardening guide](https://datapasa.com/wiki/server-management/secure-vps-ufw-fail2ban). Keep in mind that Docker routes traffic to published container ports before UFW gets to look at it. A container started with `-p 5432:5432` is reachable from the whole internet even if `ufw status` shows port 5432 as closed.

The fix is to say which address a port should listen on. For anything that only needs to be reached from the server itself, through a reverse proxy, or through an SSH tunnel, bind it to the loopback address:

```bash
docker run -d --name cache -p 127.0.0.1:6379:6379 redis:7
```

In a Compose file the same idea looks like `"127.0.0.1:6379:6379"` under `ports`. Leave only your reverse proxy on ports 80 and 443 open to the world. To see what is exposed, run `sudo ss -tlnp` and look for `0.0.0.0` or `[::]` in the local address column.

### nftables and iptables

Debian 12 and 13 use nftables in the kernel, and their `iptables` command is the `iptables-nft` wrapper that writes nftables rules. Docker works with this setup out of the box. Two things are worth knowing:

- Docker supports custom rules only when they are added with `iptables` or `ip6tables` (into the `DOCKER-USER` chain). Rules you write directly with `nft` are not supported alongside Docker.
- Debian's default `/etc/nftables.conf` begins with `flush ruleset`. If you enable the `nftables` service and restart it later, it erases the rules Docker created, and containers lose network access until you run `sudo systemctl restart docker`.

If you do not manage firewall rules by hand, you can ignore this section.

## Troubleshooting

**`permission denied while trying to connect to the Docker daemon socket`.** Your user is not in the `docker` group yet, or the current session started before you were added. Run `id` and look for `docker` in the list. If it is missing, log out and back in.

**`WARNING: Error loading config file: /home/USER/.docker/config.json: permission denied`.** You ran `sudo docker` before joining the group, and the config folder now belongs to root. Give it back to your user:

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

**`apt update` complains about the Docker repository.** A `NO_PUBKEY` or signature error means the key file is missing or unreadable: repeat the key commands in Step 2. A `does not have a Release file` error usually means the `Suites` line contains a codename Docker does not publish, such as an old Debian release or a derivative.

**`Conflicts: docker.io` or broken dependencies during install.** Debian's own packages are still installed. Run Step 1 again and then repeat the install.

**`Malformed entry` in a sources file.** You probably have both an old `docker.list` and the new `docker.sources` file. Keep only `/etc/apt/sources.list.d/docker.sources`.

**Docker does not start.** Read the last log lines with `sudo journalctl -u docker -n 50 --no-pager`. A common cause is a typo in `/etc/docker/daemon.json`. The file must be valid JSON.

**Containers have no internet access.** Restart Docker so it recreates its firewall rules: `sudo systemctl restart docker`. If this keeps happening, check whether another firewall service is flushing the rules (see the nftables note above).

## Keeping Docker updated

Because Docker now comes from an apt repository, your usual system update covers it:

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

Upgrading the daemon restarts it, so running containers stop briefly. Containers with a `restart: unless-stopped` or `restart: always` policy come back on their own. Back up container volumes before major upgrades, as described in [how to back up your VPS](https://datapasa.com/wiki/server-management/back-up-your-vps).

## FAQ

**Can I use the get.docker.com convenience script on Debian?**
It works, but Docker positions it for testing and development, and it gives you less control over what is installed. Adding the repository by hand takes two minutes and is easier to audit.

**Does Debian 11 still work?**
Docker no longer lists bullseye as a supported release, so you will not get new versions there. Upgrade the server to Debian 12 or 13, or create a new server on Debian 13 and move your containers.

**Do I need to install docker-compose separately?**
No. The `docker-compose-plugin` package provides `docker compose`. Debian's `docker-compose` package is a separate, older program and conflicts with Docker CE.

**Is Docker enabled at boot?**
Yes. On Debian both `docker` and `containerd` are enabled during installation. If you disabled them earlier, run `sudo systemctl enable docker containerd`.

**How do I remove Docker again?**
See [how to uninstall Docker completely](https://datapasa.com/wiki/server-management/uninstall-docker), which covers the packages, the data folders and the repository.
