Docker CE gives Debian hosts the container engine most Compose projects, CI runners, build pipelines, and vendor deployment notes expect. The Docker packages from Docker’s official APT repository include Docker Engine, the docker CLI, containerd.io, Buildx, and the Docker Compose V2 plugin, so one APT-managed install covers the normal server and developer workflow.
The Docker repository currently supports Debian 13 (Trixie), Debian 12 (Bookworm), and Debian 11 (Bullseye) for Docker Engine. The setup uses Debian’s DEB822 source format with a scoped keyring, checks the package source before installation, explains non-root and rootless choices, and adds practical first-use examples so the system is ready for real containers rather than only a version check.
Prepare Debian for Docker CE Installation
Start by confirming your Debian release, CPU architecture, package state, and sudo access. Docker’s Debian repository must match the Debian codename on the host; using an Ubuntu repository URL with a Debian codename such as trixie or bookworm causes APT release-file errors.
Check Debian Release and Architecture
Read the Debian codename from /etc/os-release, which is present on standard, minimal, and server installations:
. /etc/os-release
printf '%s %s\n' "$PRETTY_NAME" "$VERSION_CODENAME"
dpkg --print-architecture
Supported Debian codenames for this workflow are trixie, bookworm, and bullseye. Docker’s Debian repository publishes packages for common 64-bit server architectures such as amd64 and arm64, along with additional supported Debian architectures. It does not publish an i386 package path, so 32-bit x86 systems should use another container strategy or a supported 64-bit Debian installation.
Debian derivatives need extra care. If you run Kali, LMDE, Raspberry Pi OS, Armbian, or another derivative, use the matching Debian base codename only after confirming Docker supports that base release and your architecture.
Update Debian Packages
Refresh APT metadata and apply pending Debian updates before adding Docker’s repository:
sudo apt update
sudo apt upgrade
The commands assume your account can use sudo. If the shell reports that your user is not in the sudoers file, switch to the root account or follow the Debian sudo setup in how to add a user to sudoers on Debian.
Remove Conflicting Docker Packages
Debian can provide docker.io, docker-compose, containerd, and runc packages from Debian-owned repositories. Docker CE uses Docker’s own package set, including containerd.io, so remove the conflicting packages before adding the official Docker source:
packages=$(dpkg-query -W -f='${binary:Package}\n' docker.io docker-doc docker-compose podman-docker containerd runc 2>/dev/null || true)
if [ -n "$packages" ]; then
sudo apt remove $packages
else
printf 'No conflicting Docker packages are installed.\n'
fi
APT may report that some packages were not installed. That is normal on a fresh Debian system. Removing packages does not delete existing images, containers, volumes, or networks under /var/lib/docker/; destructive data cleanup belongs in the reset or uninstall sections after you have decided whether to preserve existing workloads.
Add Docker CE APT Repository on Debian
Docker CE should come from Docker’s official Debian APT repository when you want current Engine, Buildx, and Compose packages. The manual DEB822 setup mirrors Docker’s Debian installation instructions and keeps the source file easy to inspect. The extrepo method remains useful when you already manage third-party repositories through Debian’s extrepo tooling.
| Method | Best fit | Update behavior |
|---|---|---|
| Official Docker DEB822 source | Most servers, scripts, and current Docker documentation | APT-managed updates from Docker’s repository |
| extrepo on Debian | Systems that already standardize third-party source management with extrepo | APT-managed updates from the same Docker repository |
Use one repository setup method only. Mixing extrepo and a manual Docker source can create duplicate source entries or conflicting Signed-By values. If you switch methods, remove the old Docker source file before running sudo apt update.
Do not use sudo apt install docker.io when the goal is Docker CE from Docker Inc. Debian’s docker.io package follows Debian’s packaging cadence and uses a different package surface. Docker’s repository provides the package names used by current Docker documentation: docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, and docker-compose-plugin.
Method 1: Configure Docker DEB822 Source
Install the small helper packages needed to download Docker’s key over HTTPS and create an APT source file:
sudo apt install ca-certificates curl
Create the keyring directory, download Docker’s current Debian signing key, and make the key readable by APT:
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
The Docker key currently identifies as Docker Release (CE deb) <docker@docker.com> with fingerprint 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88. If gpg is installed, inspect the saved key locally before trusting the source:
if command -v gpg >/dev/null 2>&1; then
gpg --show-keys --with-fingerprint /etc/apt/keyrings/docker.asc
else
printf 'Install gpg if you want to inspect key details locally.\n'
fi
Create the Docker source file in DEB822 format. The command reads the Debian codename from /etc/os-release and the architecture from dpkg:
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
Refresh APT metadata after adding the source:
sudo apt update
Confirm that APT now sees docker-ce from Docker’s Debian repository, not from another source:
apt-cache policy docker-ce
Healthy output shows your Debian codename and a Docker package candidate. Version numbers change frequently, so focus on the source URL, suite, and package availability:
docker-ce:
Installed: (none)
Candidate: 5:29.x.x-1~debian.13~trixie
Version table:
5:29.x.x-1~debian.13~trixie 500
500 https://download.docker.com/linux/debian trixie/stable amd64 Packages
Method 2: Enable Docker CE with extrepo
The extrepo data set includes Docker CE for Debian trixie, bookworm, and bullseye. Use this method when extrepo is already part of your Debian repository workflow and you prefer it to own the source file and embedded key material.
sudo apt install extrepo curl
sudo extrepo enable docker-ce
sudo apt update
Verify the package candidate the same way:
apt-cache policy docker-ce
Do not create /etc/apt/sources.list.d/docker.sources manually after using extrepo. The extrepo source file normally lives under /etc/apt/sources.list.d/extrepo_docker-ce.sources, and keeping both files active can break APT updates.
Review Docker Firewall and Access Behavior
Docker creates firewall rules for bridge networks, NAT, isolation, and published ports. That behavior is expected, but it surprises many Debian administrators because a published container port can be reachable even when a host firewall rule appears to block the same port.
Docker’s firewall documentation warns that Docker and UFW handle rules in incompatible ways for published container ports. If you run docker run -p 8080:80 nginx, traffic to port 8080 is routed through Docker’s rules before UFW’s normal INPUT and OUTPUT checks apply. Docker also expects firewall rules that interact with Docker networking to use iptables or ip6tables, not direct nft rules.
For a private development service, bind the published port to localhost instead of every interface:
docker run --rm -p 127.0.0.1:8080:80 nginx:alpine
For public services, decide which layer owns access control before deployment. Host firewall tools such as UFW on Debian or firewalld on Debian can still protect normal host services, but container-published ports need Docker-aware rules, careful bind addresses, a reverse proxy, or rules in Docker’s DOCKER-USER chain.
Install Docker CE on Debian
After the Docker source is active and verified, install the Engine, CLI, container runtime, Buildx plugin, and Compose V2 plugin in one transaction:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The package roles are worth knowing before troubleshooting later: docker-ce provides the daemon, docker-ce-cli provides the client command, containerd.io provides the container runtime used by Docker Engine, docker-buildx-plugin enables modern BuildKit workflows, and docker-compose-plugin provides the docker compose subcommand.
Check the installed client version:
docker --version
Docker version 29.x.x, build xxxxxxx
Check that the systemd service is active. Docker normally starts automatically after installation on Debian-based systems, but the explicit status check catches disabled services, failed daemon configuration, and minimal image differences early:
systemctl status docker.service
docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
Active: active (running)
Docs: https://docs.docker.com
Start the service manually if the status is inactive but no configuration error is shown:
sudo systemctl start docker.service
Run Docker’s small test image to prove that the client can reach the daemon, the daemon can pull from Docker Hub, and a container can start:
sudo docker run --rm hello-world
Hello from Docker! This message shows that your installation appears to be working correctly.
Set Up Docker for Daily Use
Docker works immediately with sudo docker, but most developer workstations and CI-style shell sessions need a deliberate choice between the rootful daemon with docker group access and rootless mode. Servers can also keep sudo docker only, especially when few trusted administrators need direct access.
Allow a Trusted User to Run Docker
The docker group gives root-level control over the host because containers can mount host paths, change files, and interact with the daemon socket. Add only trusted users. For stronger privilege separation, skip this step and use rootless mode later.
sudo usermod -aG docker "$USER"
Log out and log back in so Linux re-evaluates group membership. For the current terminal only, start a new shell with the updated group:
newgrp docker
Verify that the user can run Docker without sudo:
docker run --rm hello-world
Check Docker Compose V2
Compose V2 is installed as a Docker CLI plugin through the docker-compose-plugin package. Use docker compose with a space; the old standalone docker-compose command is the legacy V1 naming pattern.
docker compose version
Docker Compose version vX.Y.Z
If a project still documents docker-compose up -d, try the V2 form first:
docker compose up -d
Run a Local Web Container
A quick Nginx container proves port publishing and container lifecycle commands without creating persistent data. The port binds to 127.0.0.1 so it is reachable only from the Debian host:
The first line removes a leftover container from an earlier run of this example, which keeps the test repeatable if you run it more than once:
docker rm -f lc-nginx-test 2>/dev/null || true
docker run -d --name lc-nginx-test -p 127.0.0.1:8080:80 nginx:alpine
docker ps --filter name=lc-nginx-test
for attempt in 1 2 3 4 5; do
if curl -fsI http://127.0.0.1:8080; then
break
fi
if [ "$attempt" -eq 5 ]; then
exit 1
fi
sleep 1
done
HTTP/1.1 200 OK
Remove the test container after confirming the service responds:
docker stop lc-nginx-test
docker rm lc-nginx-test
Create a Small Compose Project
Compose is useful when a service needs repeatable options, mounted data, environment variables, or multiple containers. This minimal project starts the same Nginx image with a local-only port binding:
mkdir -p ~/lc-docker-compose-demo
cd ~/lc-docker-compose-demo
cat > compose.yaml <<'EOF'
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:8080:80"
EOF
docker compose up -d
docker compose ps
Check the container with the same short readiness loop, then stop the Compose project and delete the demo directory when finished:
for attempt in 1 2 3 4 5; do
if curl -fsI http://127.0.0.1:8080; then
break
fi
if [ "$attempt" -eq 5 ]; then
exit 1
fi
sleep 1
done
docker compose logs --tail=20 web
docker compose down
cd ~
rm -rf ~/lc-docker-compose-demo
For projects that clone source repositories during image builds, install Git on Debian on the host or inside the build image, depending on where the clone happens.
Manage Docker Service and Configuration
Docker Engine installs as a systemd service named docker.service. The runtime package also provides containerd.service. Most users leave both enabled on servers and development machines that run containers regularly.
Start, Stop, or Restart Docker
sudo systemctl start docker.service
sudo systemctl stop docker.service
sudo systemctl restart docker.service
Enable or disable automatic startup for Docker and containerd together:
sudo systemctl enable docker.service containerd.service
sudo systemctl disable docker.service containerd.service
Configure Docker Log Rotation
Docker’s default json-file log driver can grow large on noisy containers. On a new Docker host, create /etc/docker/daemon.json with a default log limit. If the file already contains settings, merge the log-driver and log-opts keys instead of replacing the file.
sudo install -m 0755 -d /etc/docker
sudo tee /etc/docker/daemon.json <<'EOF'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
Validate the JSON before restarting Docker:
python3 -m json.tool /etc/docker/daemon.json
Restart Docker after the file validates:
sudo systemctl restart docker.service
Check Docker Disk Usage
Images, stopped containers, build cache, and unused volumes can consume significant disk space on long-lived Debian hosts. Inspect usage before pruning:
docker system df
Remove unused containers, networks, dangling images, and build cache only after checking that no team workflow depends on them:
docker system prune
Named volumes are excluded from that command unless you add --volumes. Treat volume pruning as destructive because databases and application state often live there.
Use Docker Rootless Mode on Debian
Rootless mode runs the Docker daemon and containers inside a user namespace as an unprivileged user. It reduces the risk created by rootful daemon access and the docker group, but it changes networking, port binding, storage, and service management behavior. Use it for development, CI workers, or user-owned workloads that do not need the normal rootful daemon.
Install the rootless prerequisites and confirm your user has subordinate UID and GID ranges. The uidmap package provides newuidmap and newgidmap, which Docker rootless mode needs for user namespaces:
sudo apt install uidmap
grep "^$(whoami):" /etc/subuid /etc/subgid
/etc/subuid:username:100000:65536 /etc/subgid:username:100000:65536
If the ranges are missing, create them with usermod. Replace username with the account that will run rootless Docker, and choose a different unused range if the sample range already belongs to another user:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 username
Install the rootless extras package if the setup tool is not already present:
sudo apt install docker-ce-rootless-extras
Disable the rootful daemon when you want rootless Docker to be the only active local Docker daemon:
sudo systemctl disable --now docker.service docker.socket
sudo rm -f /var/run/docker.sock
Run the setup tool as the non-root user, not with sudo:
dockerd-rootless-setuptool.sh install
The tool creates a user-level systemd service and a rootless Docker context. Enable lingering if the rootless daemon should continue running after the user logs out:
sudo loginctl enable-linger "$USER"
systemctl --user status docker.service
Some shells and applications need DOCKER_HOST set explicitly. Add it to your shell profile only for the user that owns the rootless daemon:
printf '%s\n' 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> ~/.bashrc
source ~/.bashrc
Confirm that the client is using the rootless context and that the server reports the rootless security option:
docker context ls
docker info | grep -i rootless
rootless
Rootless mode cannot bind low ports such as 80 and 443 without extra configuration, and some networking and storage paths differ from rootful Docker. For public web services, many administrators keep rootful Docker behind a reverse proxy such as Nginx on Debian and limit direct daemon access to trusted users.
Upgrade Docker CE on Debian
Docker packages installed from Docker’s repository update through APT like other packages from configured sources. Use the normal Debian update flow for routine upgrades:
sudo apt update
sudo apt upgrade
Limit the transaction to Docker packages when you want to upgrade Docker without applying every available Debian update in the same step:
sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
Check the versions after an upgrade:
docker --version
docker compose version
apt-cache policy docker-ce
Docker’s repository also exposes older versions through APT. For production pinning or rollback, list available versions and select a version string deliberately rather than guessing one from another Debian release:
apt list --all-versions docker-ce
Uninstall Docker CE from Debian
Uninstalling Docker has three separate surfaces: packages, workload data, and repository configuration. Remove workload data only when you no longer need local images, containers, volumes, build cache, or Docker-managed networks.
Remove Docker Packages
Stop Docker and remove the Docker CE package set:
sudo systemctl stop docker.service docker.socket 2>/dev/null || true
sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo apt autoremove
Remove Docker Data
Warning: The next commands permanently delete local Docker images, containers, volumes, networks, and build cache. Back up important volumes or bind-mounted application data before removing these paths.
sudo rm -rf /var/lib/docker
Delete /var/lib/containerd only after confirming no remaining containerd package uses it. This matters on hosts that also run Kubernetes, nerdctl, or another container runtime:
dpkg-query -W -f='${binary:Package}\n' containerd containerd.io 2>/dev/null
If the command prints no installed package names and Docker was the only consumer, remove the containerd state directory:
sudo rm -rf /var/lib/containerd
Remove Docker Repository Files
For the manual DEB822 method, remove the Docker source and key:
sudo rm -f /etc/apt/sources.list.d/docker.sources
sudo rm -f /etc/apt/keyrings/docker.asc
For extrepo, disable the repository first. This keeps extrepo’s record but marks the Docker source inactive:
sudo extrepo disable docker-ce
Remove extrepo’s Docker source file and key only when you want to delete the local source record completely and no other source uses that Docker key:
sudo rm -f /etc/apt/sources.list.d/extrepo_docker-ce.sources
sudo rm -f /var/lib/extrepo/keys/docker-ce.asc
Remove Docker daemon configuration only if it was created for this Docker installation and you no longer need the settings:
sudo rm -f /etc/docker/daemon.json
Refresh APT metadata so package lookups no longer use stale Docker repository data:
sudo apt update
Confirm Docker CE packages are removed and the command is no longer available from the removed source:
dpkg-query -W -f='${binary:Package}\t${Version}\n' 'docker-ce*' 'docker-compose-plugin' 'docker-buildx-plugin' 'containerd.io' 2>/dev/null
command -v docker || true
apt-cache policy docker-ce
Troubleshoot Docker on Debian
Most Docker install failures on Debian come from a wrong repository URL, unsupported architecture, duplicate source files, stale key material, daemon configuration errors, or permissions around the Docker socket. Work through the symptom that matches your output instead of reinstalling blindly.
APT Says Docker CE Has No Installation Candidate
Check which Docker source files APT sees:
grep -R "download.docker.com/linux" /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources 2>/dev/null
A Debian host should use https://download.docker.com/linux/debian. If the URL says linux/ubuntu with a Debian codename, APT can fail with a release-file error such as:
E: The repository 'https://download.docker.com/linux/ubuntu trixie Release' does not have a Release file.
Use the grep output to identify and remove the wrong Docker source file. After recreating the Debian source with the DEB822 method, refresh APT and check the package candidate again:
sudo apt update
apt-cache policy docker-ce
APT Reports an Unsupported i386 Package Path
If APT mentions stable/binary-i386/Packages, the source file is asking Docker’s repository for an architecture Docker does not publish. Check your source file’s Architectures: field and the host architecture:
dpkg --print-architecture
grep -R "^Architectures:" /etc/apt/sources.list.d/*.sources 2>/dev/null
Recreate the Docker source file using Architectures: $(dpkg --print-architecture) on a supported Debian architecture. A 32-bit x86 Debian host cannot install Docker CE from Docker’s Debian repository.
APT Reports Conflicting Signed-By Values
Duplicate Docker source files often cause this error. Inspect all Docker source entries:
grep -R "download.docker.com/linux/debian" /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources 2>/dev/null
Keep either the manual file or the extrepo file. Remove the manual file when extrepo should own Docker:
sudo rm -f /etc/apt/sources.list.d/docker.sources
sudo rm -f /etc/apt/keyrings/docker.asc
sudo apt update
Or remove the extrepo file when the manual Docker source should own the repository:
sudo rm -f /etc/apt/sources.list.d/extrepo_docker-ce.sources
sudo apt update
APT Reports Docker GPG Signature Errors
For the manual method, refresh Docker’s key and run APT again:
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
sudo apt update
For extrepo-managed systems, update Debian package metadata and the extrepo package before changing source files manually:
sudo apt update
sudo apt install --only-upgrade extrepo
Docker Commands Need sudo or Return Permission Denied
A Docker socket permission error means the user is not connected to a rootful Docker daemon with enough privileges:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
Check group membership and activate the Docker group if you intentionally use the rootful daemon:
id -nG "$USER"
sudo usermod -aG docker "$USER"
newgrp docker
If Docker config files under your home directory were created with sudo docker, repair ownership:
sudo chown -R "$USER:$USER" "$HOME/.docker"
sudo chmod -R u+rwX,go-rwx "$HOME/.docker"
Docker Service Fails to Start
Read the daemon logs and validate daemon.json if you changed Docker configuration:
sudo journalctl -xeu docker.service
python3 -m json.tool /etc/docker/daemon.json
Invalid JSON commonly produces daemon startup errors. Fix the file, then restart Docker:
sudo systemctl restart docker.service
systemctl status docker.service
Containers Cannot Resolve DNS
Separate general network failure from DNS failure by testing an IP address and a hostname inside a temporary container:
docker run --rm alpine ping -c 3 1.1.1.1
docker run --rm alpine nslookup deb.debian.org
If IP connectivity works but DNS fails, merge a DNS setting into /etc/docker/daemon.json and restart Docker. Do not overwrite an existing production configuration without preserving its other keys:
{
"dns": ["1.1.1.1", "8.8.8.8"]
}
sudo systemctl restart docker.service
Docker Bridge Network Conflicts with a LAN
Docker’s default bridge networks often use private ranges such as 172.17.0.0/16. If your LAN or VPN uses the same range, containers may lose access to internal services. Inspect Docker networks and host routes:
docker network ls
docker network inspect bridge
ip route
Set a non-overlapping default address pool in /etc/docker/daemon.json, merging with any existing settings:
{
"default-address-pools": [
{"base": "10.200.0.0/16", "size": 24}
]
}
Restart Docker and remove unused networks after confirming no running workload depends on them:
sudo systemctl restart docker.service
docker network prune
Docker Desktop Is Not the Same as Docker Engine
Docker Desktop for Debian is a separate GUI product with its own requirements, subscription terms, contexts, and update flow. Docker Engine is the standard target for Debian servers, headless hosts, SSH-managed systems, and most Compose deployments. Docker currently documents 64-bit Debian 12 as the Debian Desktop requirement, so Debian 13 workstation users should verify current Desktop support before installing the Desktop DEB package. Do not replace this Engine workflow with a random docker-desktop-amd64.deb download on a server.
Conclusion
Docker CE is now installed from Docker’s Debian repository with Compose V2, Buildx, service management, safer user access choices, and cleanup paths covered. Keep published ports deliberate, review disk usage regularly, and use rootless mode when the workload benefits from a user-owned daemon. For common next steps, secure containerized web apps behind Nginx with Let’s Encrypt on Debian and manage remote administration with SSH on Debian.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>