How to Install Docker Compose on Ubuntu 25.10

Install Docker Compose on Ubuntu 25.10 with Ubuntu’s Docker packages, run a two-service stack, verify it, and fix common setup errors quickly.
A polished 3D illustration of container blocks and a server rack with cyan light trails and an orange Ubuntu-inspired accent.

Install Docker Compose on Ubuntu 25.10 with Ubuntu’s packaged Docker Engine and Compose v2 plugin, then launch a two-service test stack in about ten minutes. You will finish with a repeatable compose.yaml project whose web port stays private to the VPS.

Prerequisites

  • A 64-bit Ubuntu 25.10 (Questing Quokka) 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 Ubuntu package mirrors and Docker Hub.
  • A VPSLake Linux VPS if you need an Ubuntu server with root access.
  • No domain is needed for this local test. Add a domain later when you put a public application behind a TLS reverse proxy.

The commands use sudo for a regular SSH account. If you are logged in as root, remove sudo. Ubuntu 25.10 is an interim release; for a new production VPS, choose a supported LTS release instead.

Step 1: Confirm Questing and refresh APT

Checking the codename first prevents you from installing packages from the wrong Ubuntu release.

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

The release should report VERSION_CODENAME=questing and a supported architecture such as amd64 or arm64:

PRETTY_NAME="Ubuntu 25.10 (Questing Quokka)"
VERSION_CODENAME=questing
amd64

Ubuntu 25.10 is not currently listed among Docker’s supported releases in Docker’s Ubuntu Engine installation guide. Do not change questing to noble in a Docker source file: mixing release repositories can replace core dependencies with incompatible versions.

Step 2: Install Ubuntu’s Docker Engine and Compose v2 packages

Ubuntu maintains docker.io and the docker-compose-v2 plugin in its own repositories, so this release does not need Docker’s third-party APT repository.

sudo apt install -y docker.io docker-compose-v2
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. Package versions change as Ubuntu publishes updates:

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

Run Docker’s small smoke test before creating a project. It downloads an image, runs a container, prints a confirmation, and then exits:

sudo docker run --rm hello-world

The output should include:

Hello from Docker!
Your installation appears to be working correctly.

Step 3: Let your SSH user run Docker

The daemon socket is root-owned by default. Adding your account to the docker group avoids sudo on every Compose command, but that group grants root-equivalent control of the server.

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

Disconnect and open a new SSH session so the group change is loaded. Then check access without sudo:

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

The group list should contain docker, and the second command should print the Docker server version. Use sudo docker compose ... until you reconnect, or run newgrp docker to start a shell with the new membership. Treat this group as administrative access; use rootless Docker when you need a stronger boundary.

Step 4: Create a Compose project

Compose keeps service definitions, configuration, and persistent storage in one file. This example serves a static page with Caddy and starts PostgreSQL on Compose’s private network; neither service is exposed on a public interface.

Create the project directory and page:

mkdir -p ~/compose-questing/site
cd ~/compose-questing
printf '%s\n' 'Docker Compose on Ubuntu 25.10 is running.' > site/index.html

Create a Caddy configuration that serves that directory:

tee Caddyfile >/dev/null <<'EOF'
:80 {
    root * /srv
    file_server
}
EOF

Create a protected environment file. Replace the placeholder with a long, unique password before starting the stack; it is not a usable credential:

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

Now create compose.yaml. The database has no host port, so only the web container can reach it through the private Compose network.

tee compose.yaml >/dev/null <<'YAML'
services:
  web:
    image: caddy:2-alpine
    ports:
      - "127.0.0.1:8089:80"
    volumes:
      - ./site:/srv:ro
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
    depends_on:
      db:
        condition: service_healthy

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

volumes:
  postgres_data:
YAML

The read-only site mount protects the host files, while the named volume keeps PostgreSQL data when its container is recreated. The depends_on condition makes Caddy wait until PostgreSQL reports healthy; it does not publish or otherwise secure a database for internet access. Docker’s Compose file reference explains the service, volume, and health-check keys used here.

Step 5: Validate and start the stack

Render the file before pulling images so YAML errors, missing variables, and indentation mistakes fail early.

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

config --quiet should produce no output and return status 0. After the first image download, both services should be running and the database should be healthy:

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

Container names can vary if you choose a different directory. Use the SERVICE column, such as web or db, in later commands.

Verify Docker Compose on Ubuntu 25.10

Check the page, query PostgreSQL’s readiness probe, and review recent logs. Together, these checks prove that Compose rendered the file, started both services, and kept the database private.

curl --fail --silent --show-error http://127.0.0.1:8089
docker compose exec db pg_isready -U compose_app -d compose_demo
docker compose logs --tail 20 web db

The first two commands should return the page text and a readiness message:

Docker Compose on Ubuntu 25.10 is running.
/var/run/postgresql:5432 - accepting connections

Port 8089 is bound to the VPS loopback address, so a browser on your workstation cannot reach it directly. For private browser testing, create an SSH tunnel from your workstation with ssh -L 8089:127.0.0.1:8089 USER@SERVER_IP, then open http://127.0.0.1:8089 locally. If you later publish an application, use TLS and a deliberate Docker-aware firewall policy; Docker documents that published container ports can bypass ordinary UFW filtering. VPSLake’s UFW firewall generator can help draft the host rules.

Troubleshooting

Package 'docker-compose-v2' has no installation candidate

The Universe component may be disabled, or APT may still have stale metadata. Enable the component and retry:

sudo apt install -y software-properties-common
sudo add-apt-repository -y universe
sudo apt update
sudo apt install -y docker.io docker-compose-v2

If APT instead reports that a Questing mirror returns 404, see the next problem. Do not install a Noble package to work around a missing Questing index.

404 Not Found or does not have a Release file for Questing

Ubuntu 25.10 is an interim release, and its standard support ended in July 2026. Normal mirrors may no longer carry its packages. For a production server, migrate to a supported Ubuntu LTS image; do not point a Questing host at Noble or Resolute repositories. If this is an existing machine you must preserve, follow Ubuntu’s release-upgrade guidance before continuing.

permission denied while trying to connect to the Docker daemon socket

Your current SSH session has not loaded the new group membership, or the command is running as a different user. Reconnect, run id -nG, and retry. sudo docker compose ... is a temporary workaround, but remember that docker group membership is effectively root access.

dependency failed to start or db is unhealthy

The database may still be initializing, or the .env file still contains the placeholder password. Check the value and inspect the service log:

grep -n '^POSTGRES_PASSWORD=' .env
docker compose logs db
docker compose ps

Replace YOUR_STRONG_POSTGRES_PASSWORD with a real value containing no spaces around the equals sign, then run docker compose up -d again. For this disposable example only, docker compose down -v removes the named database volume; never use -v when it contains data you need.

Hardening

  • Keep application ports on 127.0.0.1 until a TLS reverse proxy, authentication, and Docker-aware firewall policy are ready.
  • Never expose an unauthenticated Docker API on port 2375 or 2376.
  • Back up postgres_data and record the .env secret in a password manager; do not commit either file to a public repository.
  • Update images deliberately with docker compose pull and docker compose up -d, and pin reviewed image tags or digests for production.

FAQ

Why does this guide use Ubuntu packages instead of Docker’s APT repository?

Docker’s current Ubuntu installation page lists supported LTS releases, not Ubuntu 25.10. Ubuntu’s docker.io and docker-compose-v2 packages are built for Questing, so using them avoids pretending that a different release is supported.

Should I use docker compose or docker-compose?

Use docker compose with a space. It is Compose v2 provided as a Docker CLI plugin; the hyphenated docker-compose command is the older standalone interface and is not needed here.

Does Compose create a new VPS or virtual machine?

No. Compose creates and manages containers, networks, and volumes on the Docker host. The compose.yaml file describes those resources so you can recreate the application consistently on another Docker server.

How do I stop the example without deleting PostgreSQL data?

Run docker compose down from ~/compose-questing; it removes the containers and network but leaves the named volume. Avoid docker compose down -v unless you intentionally want to erase the example database.

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

How to Fix Remote Desktop Connection Error Code 0x204

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 ✨