How to Install Docker Compose on Debian 12

Install Docker Compose on Debian 12 with Docker’s official repository, run a two-service stack, verify it, fix common errors, and start securely.
A polished 3D illustration of container blocks beside a server rack on a midnight-blue background with orange accents.

Install Docker Compose on Debian 12 using Docker’s current Compose v2 plugin, then launch and manage a small web-and-cache stack. The setup takes about ten minutes and leaves you with commands you can reuse for real multi-container applications.

Prerequisites

  • A 64-bit Debian 12 (Bookworm) VPS with at least 2 GB of RAM and 10 GB of free disk space.
  • SSH access as root, or a normal account with working sudo privileges.
  • Outbound HTTPS access to Docker’s package repository and Docker Hub.
  • A VPSLake Linux VPS if you need a Debian server with root access.
  • A domain is not required for this local verification example. Add one later when putting a TLS reverse proxy in front of a public service.

The commands use sudo for a normal SSH account. If you are logged in as root, remove sudo and do not add a second sudo prefix.

Step 1: Confirm Debian 12 and update APT

Docker’s repository must use Bookworm, so confirm the release before changing APT sources.

grep -E '^(PRETTY_NAME|VERSION_CODENAME)=' /etc/os-release
dpkg --print-architecture
sudo apt update

Expected output includes VERSION_CODENAME=bookworm and a supported architecture such as amd64 or arm64:

PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
VERSION_CODENAME=bookworm
amd64

Do not continue while apt update reports a broken mirror, DNS failure, or expired repository. Docker’s packages cannot fix an unhealthy Debian package configuration.

Step 2: Add Docker’s signed Debian repository

The dedicated keyring and deb822 source file avoid the deprecated apt-key workflow and let APT verify Docker packages.

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
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<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

Confirm that APT can see a Docker Engine candidate for Bookworm:

apt-cache policy docker-ce

The candidate should come from download.docker.com and show bookworm in its repository line. Docker lists the supported Debian releases and the repository method in its official Debian installation guide.

Step 3: Install Docker Engine and Compose v2

Compose is installed as docker compose, a subcommand provided by Docker’s Compose plugin; the old standalone docker-compose command is not needed.

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo systemctl is-active docker
sudo docker version --format 'Client {{.Client.Version}} / Server {{.Server.Version}}'
sudo docker compose version

You should see an active service, matching client and server versions, and a Compose v2 version. Exact numbers change as Docker publishes updates:

active
Client 29.x.y / Server 29.x.y
Docker Compose version v2.x.y

If Docker was already installed, you can skip the repository and package commands, but still run docker compose version. If the command is missing, install docker-compose-plugin from the same Docker repository.

Step 4: Allow a non-root account to run Compose

Docker’s Unix socket is root-owned by default. The docker group removes repeated sudo prompts, but membership grants root-equivalent control over the VPS.

Run this for the Linux account that will manage the project:

sudo groupadd --force docker
sudo usermod -aG docker "$USER"

Disconnect from SSH and reconnect so the new group is loaded. Then check the group and daemon connection:

id -nG
docker info --format 'Server {{.ServerVersion}}'

The first command should include docker. Until you reconnect, prefix Docker commands with sudo or run newgrp docker in the current shell. Treat this group as administrative access; use rootless Docker when you need a stronger boundary.

Step 5: Create a Compose project

Compose reads a YAML file to define services, storage, and networking. This example runs Nginx on loopback and Redis with a named volume, so it demonstrates two services without exposing a new public port. The Compose file reference documents the available keys when you extend this example.

Create a project directory and a page for Nginx:

mkdir -p ~/compose-demo/site
cd ~/compose-demo
printf '%s\n' 'Docker Compose on Debian 12 is working.' > site/index.html

Create compose.yaml:

tee compose.yaml >/dev/null <<'YAML'
services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./site:/usr/share/nginx/html:ro
    depends_on:
      - cache

  cache:
    image: redis:7-alpine
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

volumes:
  redis_data:
YAML

The web service maps only to 127.0.0.1, while cache stays on Compose’s private network. The named volume keeps Redis data outside the container lifecycle; the :ro suffix prevents Nginx from changing the website files.

Step 6: Validate and start the stack

Validate the fully resolved configuration before downloading images or creating containers.

docker compose config
docker compose pull
docker compose up -d

config should print the two services and redis_data without a YAML error. The first pull may take a few minutes; up -d returns control of the terminal while both services run in the background.

Check the service state:

docker compose ps

Expected output is similar to this, with both services showing Up or running:

NAME                  IMAGE          SERVICE   STATUS       PORTS
compose-demo-web-1    nginx:alpine   web       Up           127.0.0.1:8080->80/tcp
compose-demo-cache-1  redis:7-alpine cache     Up

The generated container names can differ if you choose another project directory. Use the SERVICE column when selecting a service in later commands.

Verify Docker Compose on Debian 12

Verify both the HTTP response and the Redis service, then inspect the project logs if either check fails.

curl --fail --silent --show-error http://127.0.0.1:8080
docker compose exec cache redis-cli ping
docker compose logs --tail 20

The first two commands should return the page text and PONG:

Docker Compose on Debian 12 is working.
PONG

The web port is deliberately reachable only from the VPS itself. To make a service public, change the mapping to "8080:80", allow that exact port in your firewall, and put a TLS reverse proxy in front of it. Docker warns that published ports can bypass ordinary UFW rules; plan the DOCKER-USER policy before exposing production services. VPSLake’s UFW firewall generator can help draft a host rule set.

Troubleshooting

docker: 'compose' is not a docker command

The Compose plugin is missing, or the CLI is using a different installation than the one managed by Docker’s repository. Check the package and install it with APT:

apt-cache policy docker-compose-plugin
sudo apt install -y docker-compose-plugin
docker compose version

Use docker compose with a space. The legacy hyphenated docker-compose binary is a separate tool and is not required for this guide.

permission denied while trying to connect to the Docker daemon socket

Your current SSH session does not yet know about the docker group, or the account was not added to it. Reconnect, run id -nG, and retry. sudo docker compose ... is a temporary workaround while you arrange the session change.

E: Package 'docker-ce' has no installation candidate

APT has not loaded the Docker Bookworm index, often because the source file has the wrong codename or an earlier apt update failed. Inspect the source and refresh metadata:

cat /etc/apt/sources.list.d/docker.sources
sudo apt update
apt-cache policy docker-ce

The file must contain Suites: bookworm, and apt update must finish without an error. Do not mix a Debian docker.io installation with Docker’s docker-ce packages; remove or account for conflicting packages first.

curl: (7) Failed to connect or the web check returns connection refused

The web service may still be pulling its image, may have exited, or the port is bound only to the VPS loopback address. Review the state and logs:

docker compose ps
docker compose logs web
ss -ltnp | grep ':8080'

If web is not running, the log normally identifies a bad mount or configuration. If you are testing from your own computer, 127.0.0.1:8080 is the VPS’s loopback—not your computer’s; use an SSH tunnel or configure a protected public reverse proxy instead.

Hardening

  • Keep Docker’s daemon on its Unix socket. Never expose an unauthenticated Docker API on port 2375.
  • Keep application ports on loopback until TLS, authentication, and firewall policy are ready.
  • Back up important named volumes and use docker compose down -v only when you intentionally want to delete the project’s stored data.
  • Pin production images to reviewed tags or digests, and update them deliberately with docker compose pull followed by docker compose up -d.

FAQ

Is Docker Compose already included with Docker on Debian 12?

It is included when you install Docker Engine from Docker’s official APT repository together with docker-compose-plugin. Debian’s separate packages or an older manual Docker install may not include the plugin, so verify with docker compose version.

What is the difference between docker compose up and docker compose start?

up creates or recreates the services, networks, and volumes described by the Compose file, then starts them. start only starts existing stopped containers and does not create a missing project, so use up -d after changing configuration or setting up a project for the first time.

Does docker compose down delete the Redis data?

No. The regular down command removes the containers and default network but leaves the named redis_data volume. docker compose down -v also removes that volume, so use the -v option only when resetting the data is intentional.

How do Compose services find each other?

Compose puts services on a project network and provides DNS names matching their service keys. A container can reach the Redis service at cache:6379; it should not use the host’s localhost, which points back to the calling container.

Samrat Ghosh
Written by

Samrat Ghosh

Founder & Infrastructure Engineer

Samrat Ghosh is the founder of VPSLake, where he builds and runs the remote desktop and VPS hosting infrastructure the company is built on. He writes hands-on guides about RDP, Windows Server, VPS management and secure remote access - the practical documentation he wishes had…

Previous Article

Windows 10 End of Support: What It Means for RDP

Next Article

Remote Desktop Slow or Laggy? 10 Fixes That Help

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨