Skip to content

How to install Docker on Debian 12 and Debian 13 ​

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.

Supported Debian releases ​

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

Debian releaseCodenameStatus in Debian
Debian 13trixiestable
Debian 12bookwormoldstable

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 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.
  • SSH access. If you have not logged in yet, see how to 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.

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.

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 (the steps are the same on Debian), and keep the Compose command reference 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. 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.

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, which covers the packages, the data folders and the repository.