Deploy Multi-Container Apps in One Command with Docker Compose

Describe your whole stack in a single `compose.yaml` and bring up web + database with one command on your server.

A real web app is rarely a single container: the site needs to run, the database runs alongside it, and you might add a cache or a reverse proxy too. Wiring that up by hand with separate docker run commands—and remembering how they network and depend on each other—is tedious and error-prone. Docker Compose lets you capture the entire stack in one YAML file and start or tear it down with a single command.

What Docker Compose Is

Compose is Docker's official tool for orchestrating multiple containers. In your project folder you write a compose.yaml (the older name docker-compose.yml still works), declaring which image each service uses, which ports it exposes, what data it mounts, and what it depends on. Compose automatically puts them on a shared private network, so containers can reach each other by service name—no manual IP wiring required.

Installing the Docker Compose Plugin

Modern Compose ships as a plugin to the Docker CLI, invoked as docker compose (a space, not the legacy hyphenated docker-compose). On Ubuntu/Debian, if you installed Docker from the official repository, the plugin is usually already there. To install or add it explicitly:

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

If it prints a version number, you're good to go.

Anatomy of compose.yaml

There are only a handful of core keys:

  • services: defines each container; the key is the service name.
  • image: the image to run.
  • ports: "hostPort:containerPort", mapping a container port onto your server.
  • volumes: persists data or mounts config so nothing is lost when a container is removed.
  • environment: injects environment variables, such as database passwords.
  • dependson: declares startup-order dependencies.

Example: WordPress + MySQL

Create a compose.yaml in an empty directory:

services:
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: change-me-root
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp
      MYSQL_PASSWORD: change-me-wp

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wp
      WORDPRESS_DB_PASSWORD: change-me-wp
      WORDPRESS_DB_NAME: wordpress

volumes:
  db_data:

Notice that WORDPRESSDBHOST is set to db—the database service name, which Compose's built-in network resolves for you.

Everyday Commands

Run these from the folder that holds your compose.yaml:

docker compose up -d      # start everything in the background
docker compose ps         # check what's running
docker compose logs -f    # follow the logs live
docker compose down       # stop and remove containers and the network

The -d in up -d means detached (background). By default, down keeps named volumes, so your data survives; add -v to delete the volumes too—use that with care. After editing compose.yaml, just run docker compose up -d again and Compose recreates only the containers that changed.

Once it's up, open http://your-server-ip:8080 in a browser to reach the WordPress setup wizard.

Summary

Docker Compose turns "how these containers work together" into a single, version-controllable compose.yaml: services describe the containers, ports/volumes/environment handle mapping and data, and dependson sorts out ordering. Day to day you only need three commands—up -d to start, logs -f to watch, down to stop. Commit that file to your project repo and any server can reproduce an identical environment in one command.