How to Install Docker Compose on Debian 13

Install Docker Compose on Debian 13 with Docker’s official repository, launch an Nginx and PostgreSQL stack, verify it, and fix common errors.
A polished 3D illustration of container blocks and a server rack on a midnight-blue background with an orange Debian accent.

Install Docker Compose on Debian 13 by adding Docker’s signed repository, installing the Compose v2 plugin, and launching a small Nginx and PostgreSQL stack. You can complete the setup in about ten minutes and verify it entirely from your SSH session without exposing the database to the internet.

Prerequisites

  • A 64-bit Debian 13 (Trixie) 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.
  • Permission to download packages from Docker’s APT repository and images from Docker Hub.
  • A VPSLake Linux VPS if you need a Debian server with root access.
  • No domain is needed for this private test stack. Use a domain later if you put a TLS reverse proxy in front of a public application.

The commands use sudo for a regular SSH account. If you are logged in as root, remove sudo from the commands.

Step 1: Confirm Debian 13 and refresh APT

Docker’s repository must be configured for Trixie’s trixie codename, so confirm the release and architecture before changing package sources.

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

You should see Debian 13 and a supported architecture such as amd64 or arm64:

PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
VERSION_CODENAME=trixie
amd64

If apt update reports a broken mirror, DNS error, or unsigned repository, fix that first. Compose cannot install until Debian’s package metadata is healthy.

Step 2: Add Docker’s official repository

The keyring and deb822 source file let APT verify Docker packages without the deprecated apt-key approach.

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: trixie
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update

Check that APT has a Docker Engine candidate for Trixie:

apt-cache policy docker-ce

The candidate should come from download.docker.com and reference trixie. Docker’s Debian installation documentation lists Trixie as a supported release and documents this repository method.

Step 3: Install Docker Engine and the Compose plugin

Compose v2 is a Docker CLI plugin, so install it alongside the Engine instead of using the obsolete hyphenated docker-compose command.

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

The service check should print active, followed by Docker client/server versions and a Compose v2 version. Exact version numbers change as Docker publishes updates:

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

If Docker Engine is already installed, run the last three commands first. You only need the repository and package installation when docker compose version is missing or the Engine is not present. Avoid mixing an old docker.io installation with docker-ce; plan any migration before removing packages that may own existing containers or volumes.

Step 4: Let your SSH user run Docker

The Docker socket is root-owned by default. Adding your account to the docker group avoids repeated sudo, but that group grants root-equivalent control over the server.

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

Start a new SSH session so the group membership is loaded, then check access:

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

The group list should include docker, and docker info should print the server version. Until reconnecting, use sudo docker compose ... or run newgrp docker for the current shell.

Step 5: Create a two-service Compose project

A Compose file describes the services, network, storage, and startup relationship in one place. This example keeps Nginx on the VPS loopback address and gives PostgreSQL a private network plus a named data volume.

Create the project and a page for Nginx:

mkdir -p ~/compose-debian13/site
cd ~/compose-debian13
printf '%s\n' 'Docker Compose on Debian 13 is running.' > site/index.html

Create an environment file for the database password. Replace YOUR_STRONG_POSTGRES_PASSWORD with a long, unique password before starting the stack; it is a placeholder, not a usable credential.

tee .env >/dev/null <<'EOF'
POSTGRES_PASSWORD=YOUR_STRONG_POSTGRES_PASSWORD
EOF
chmod 600 .env

Now create compose.yaml:

services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8090:80"
    volumes:
      - ./site:/usr/share/nginx/html:ro
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
      interval: 5s
      timeout: 5s
      retries: 10

volumes:
  pg_data:

The :ro mount stops Nginx from changing the page, while pg_data keeps PostgreSQL data when containers are recreated. The database has no ports entry, so it is not published on the VPS network. See Docker’s Compose file reference when adding application-specific services or secrets.

Step 6: Validate and start the stack

Validate the rendered YAML before pulling images; this catches indentation errors and a missing password before containers are created.

docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps

config --quiet should return no output and status 0. The first image pull can take a few minutes. Both services should then show Up or running, with the web service bound to 127.0.0.1:8090 and the database marked healthy:

NAME                    SERVICE   STATUS                   PORTS
compose-debian13-web-1  web       Up                       127.0.0.1:8090->80/tcp
compose-debian13-db-1   db        Up (healthy)

Verify Docker Compose on Debian 13

Check the HTTP response, database readiness, and recent logs. These checks prove that Compose installed correctly and that both services can start together.

curl --fail --silent --show-error http://127.0.0.1:8090
docker compose exec db pg_isready -U appuser -d appdb
docker compose logs --tail 20 web db

The first command should print the page text, and PostgreSQL should report that it is accepting connections:

Docker Compose on Debian 13 is running.
/var/run/postgresql:5432 - accepting connections

The port is intentionally bound to loopback, so a browser on your own computer cannot reach it directly. If you need to inspect it remotely, use an SSH tunnel or put a TLS reverse proxy in front of the application; do not publish PostgreSQL. Docker notes that published container ports can bypass ordinary UFW rules, so use a deliberate DOCKER-USER policy before exposing a production service. VPSLake’s UFW firewall generator can help draft the host rules.

Troubleshooting

docker: 'compose' is not a docker command

The Compose CLI plugin is missing, or the shell is using a different Docker installation. Refresh APT and install the plugin from Docker’s repository:

sudo apt update
sudo apt install -y docker-compose-plugin
docker compose version

Use docker compose with a space. The standalone docker-compose binary is a separate legacy installation and is not required here.

permission denied while trying to connect to the Docker daemon socket

Your current SSH session has not loaded the new docker group, or the account was not added to it. Reconnect, run id -nG, and retry; sudo docker compose ... is a temporary workaround.

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

APT is not reading the Trixie repository, usually because the source file has the wrong suite or the last update failed. Inspect the file and refresh the index:

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

The file must contain Suites: trixie, and apt update must finish without an error. Do not replace trixie with bookworm; that installs packages for the wrong Debian release.

dependency failed to start: container db is unhealthy

Read the database log and confirm that .env contains POSTGRES_PASSWORD with no surrounding quotes or spaces:

docker compose logs db
docker compose config

If this project has never started, correct the value and run docker compose up -d again. PostgreSQL stores its initial password in the named volume, so changing .env does not change credentials in an existing database. Only for disposable test data, docker compose down -v removes that volume; never run it when the database contains data you need.

Hardening

  • Keep .env mode 600, never commit it to source control, and move production secrets to a managed secret store.
  • Keep PostgreSQL unexposed and leave the web service on 127.0.0.1 until TLS, authentication, and firewall policy are ready.
  • Back up pg_data and test restoration before treating the stack as production data.
  • Pin production images to reviewed tags or digests, then update deliberately with docker compose pull and docker compose up -d.

FAQ

Do I need the old docker-compose command on Debian 13?

No. Docker Compose v2 is provided as the docker compose CLI plugin, which is the maintained syntax used by this guide. The hyphenated command belongs to the legacy standalone tool and can create confusion when both installations exist.

Can I open the Nginx page from my laptop?

Not with the sample binding, because 127.0.0.1 refers to the VPS itself. You can create an SSH tunnel to port 8090 for private testing, or publish only a protected reverse proxy after adding TLS and an appropriate firewall policy.

Does docker compose down delete PostgreSQL data?

Regular down removes the containers and Compose network but keeps the named pg_data volume. The -v option also deletes that volume, so reserve it for a disposable reset after confirming that no data is needed.

How do I stop or restart the project later?

From ~/compose-debian13, use docker compose stop for a temporary stop and docker compose start to bring the existing containers back. Use docker compose up -d after changing the Compose file so Compose can recreate services when necessary.

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

Remote Desktop Slow or Laggy? 10 Fixes That Help

Next Article

How to Choose a Dedicated Server: 12 Specs That Matter

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 ✨