Install Docker on Debian 13 and 12 from Docker's apt repository
To install Docker on Debian, use Docker's own apt repository, not the docker.io package from Debian. This guide covers Debian 13 (trixie) and Debian 12 (bookworm), the two releases Docker currently lists as supported. You end up with Docker Engine, the docker compose plugin and automatic updates through apt.
It assumes a fresh server that you already secured with the first 15 minutes checklist. Not sure how much RAM you need for the apps you plan to run? Use the VPS size calculator first. Docker itself is small; your apps are not.
Why not apt install docker.io?
Debian ships its own docker.io package. It works, but the official packages come from Docker and follow their release schedule. Docker's docs also say the unofficial packages conflict with the official ones, so you can't have both. Pick one. I use Docker's repository because the guides on this site assume the docker compose plugin from it.
1. Remove conflicting packages
On a fresh server this usually prints that nothing is installed. Run it anyway:
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc | cut -f1)
This does not delete your images, containers or volumes in /var/lib/docker/.
2. Add Docker's apt repository
This is the block from Docker's install page for Debian:
# Add Docker's official GPG key: sudo apt update sudo apt install 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 # Add the repository to Apt sources: 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 Suites line reads your Debian codename (trixie or bookworm) from /etc/os-release. If you run Debian testing or a derivative, Docker says to put the codename of the matching Debian release there by hand, for example trixie.
Docker publishes packages for amd64, arm64, armhf and ppc64le on Debian, so this also works on an ARM VPS.
3. Install the packages
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
On Debian the service starts on boot by default, so there is nothing to enable. Check that it runs:
sudo systemctl status docker sudo docker run hello-world
hello-world downloads a tiny test image, prints a confirmation message and exits. Then check the compose plugin. Note that it is docker compose with a space, not the old docker-compose command:
docker compose version
4. Running docker without sudo (read this first)
Docker's docs describe adding your user to the docker group so you can skip sudo:
sudo usermod -aG docker $USER
Log out and back in for it to apply. The same docs warn that the docker group grants root-level privileges. Anyone in it can start a container that mounts the whole host filesystem. On a single-admin server that is a fair trade; just don't add accounts you wouldn't give root. If you want the stricter route, keep using sudo docker.
5. Docker bypasses your firewall
This is the part that catches people. Docker's own docs say that when you use ufw or firewalld and publish a container port, the port bypasses your firewall rules. Docker routes the traffic before it reaches the chains ufw manages. So ports: "8080:8080" in a compose file puts the app on the internet even with ufw default deny incoming.
What I do instead:
- Publish nothing to the outside. Let a reverse proxy reach apps over a shared Docker network, as in the Docker and Caddy setup.
- If a port must be published on the host, bind it to localhost:
127.0.0.1:8080:8080. - Docker's docs also say Docker is only compatible with
iptables-nftandiptables-legacy, and that rules created with plainnftaren't supported on a system with Docker installed. If you write your own ruleset, the firewall generator explains how to keep Docker's rules intact.
6. Limit container log size
Docker's default logging driver, json-file, writes logs to files on the host and doesn't rotate them. The docs warn this can eventually use up the disk. Docker's own example sets rotation globally in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Keep the numbers in quotes; log-opts values must be strings. Restart Docker with sudo systemctl restart docker. The change only applies to containers you create afterwards, so recreate existing ones with docker compose up -d --force-recreate. Without max-size, max-file does nothing.
Updating Docker
Docker comes from apt now, so it updates with the rest of the system:
sudo apt update && sudo apt upgrade
A new Docker Engine version can restart the daemon, which interrupts your containers, so do it in a quiet moment. Updating the apps inside containers is a separate routine; see how to update Docker containers safely.
Uninstalling
If you need to start over, Docker's documented steps are:
sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras sudo rm /etc/apt/sources.list.d/docker.sources sudo rm /etc/apt/keyrings/docker.asc
These do not touch your data. To delete all images, containers and volumes as well, remove /var/lib/docker and /var/lib/containerd. That is permanent, so back up your volumes first.
Common problems
"permission denied while trying to connect to the Docker daemon socket"
Your user isn't in the docker group yet, or you haven't logged out and in since adding it. Use sudo docker ... or see step 4.
"docker: 'compose' is not a docker command"
The docker-compose-plugin package is missing. Install it with apt. If you installed the old Debian docker-compose package, remove it (step 1).
apt can't find docker-ce
The repository file didn't get created or apt update failed. Look at /etc/apt/sources.list.d/docker.sources: the Suites line must contain a real codename and not an empty string. On testing or derivatives, put the matching Debian codename there by hand.
Next step
With Docker running, put a reverse proxy in front of your apps with the Docker and Caddy post, then pick your first app from what to self-host first. Need a cheap server for it? Check the current netcup VPS vouchers.