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.