Learning objectives
After reading this article you will be able to:
- Understand what Docker Compose solves and how it works
- Read and write a compose.yaml with an app and a database
- Know when Compose is enough and when you need another tool
Almost every real application has more than one piece: a web server, a database, maybe a cache or a queue. Starting each one with a long docker run command, remembering ports, networks and volumes, turns into a list of commands nobody can repeat without mistakes. Docker Compose exists to replace that list with a file.
You describe what the application needs in YAML, commit the file next to your code, and anyone on the team starts the same environment with one command.
What does Docker Compose do?#
Compose reads a file that declares the components of your application and creates everything they need to work together. According to the official documentation, the file has these top-level elements:
- services: the application's components. Each service runs one or more containers built from the same image and configuration.
- networks: the networks that let services talk to each other.
- volumes: persistent storage that survives container removal.
- configs and secrets: configuration and sensitive data mounted into containers as files.
- name: the project name, which groups resources and keeps separate deployments isolated.
You do not need all of them. Most files only have services and volumes.
What does a compose.yaml file look like?#
The example below starts a Node.js application that talks to PostgreSQL. The application code lives in the current folder, with a Dockerfile.
services:
web:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/app
depends_on:
db:
condition: service_healthy
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: app
volumes:
- db-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
volumes:
db-data:How to read it:
build: .builds the image from theDockerfilein the folder.image: postgres:18uses a prebuilt image.ports: "3000:3000"publishes container port 3000 on port 3000 of your machine.- Inside the network Compose creates, a service reaches another by name. That is why the URL uses
dbas the host, notlocalhost. ${DB_PASSWORD}is read from your environment or from a.envfile in the same folder, so the password is not written in the YAML. See what a .env file is.- The
db-datavolume keeps the database files even if the container is recreated. $$in thehealthcheckescapes the dollar sign, so the variable is resolved inside the container and not by Compose.
The file does not need a version: field at the top. It is obsolete, only informative, and triggers a warning if used.
Which commands do you use day to day?#
With the file in the project folder, the basic cycle is:
docker compose up -d # create networks and volumes, start services in the background
docker compose ps # show the state of each service
docker compose logs -f web # follow the logs of the web service
docker compose down # stop and remove the containers and the networkdown keeps volumes. To delete the data too, run docker compose down -v, carefully. The command is docker compose (with a space), a Docker subcommand. The older hyphenated docker-compose still shows up in old tutorials, and docker-compose.yml file names are still accepted.
If you change the code and want to rebuild the image, run docker compose up -d --build. Because Compose caches each container's configuration, services that did not change are reused.
Does depends_on wait for the database to be ready?#
By default, no. depends_on only guarantees order: Compose waits for the database container to be running, not ready to accept connections. PostgreSQL can take a few seconds to initialize, and an app that starts before it often fails with a connection error.
The fix is to declare a healthcheck on the database and use condition: service_healthy in depends_on, as in the example above. Compose then holds the web service until the database check passes. Even so, a good application retries its connection, because the database can go down later.
What is Docker Compose used for?#
The official documentation highlights three uses:
- Development environments: the database, cache, queue and APIs your project depends on start with one command, identical for the whole team.
- Automated testing: a CI pipeline creates an isolated environment, runs the suite and tears everything down.
- Single-host deployments: Compose can deploy to one host or a remote Docker host, with production-specific settings.
To tell environments apart, you can combine several files (for example a base file and a production file) or use variables, which let one configuration serve different people and environments.
When is Compose not enough?#
Compose works with one host. It does not spread containers across several servers, move a workload to another machine if the server dies, or adjust the number of replicas to demand. If you need that, the next step is a cluster orchestrator such as Kubernetes, which is also much harder to operate.
Compose describes containers, not server security. Published ports, plain-text passwords in the YAML and unpinned images (latest) are common problems. Pin image versions, keep secrets out of the repository and publish only the ports that must be reachable from outside.
Which terms get confused with Compose?#
- Dockerfile: the recipe to build an image. Compose uses it through
buildbut does not replace it. To write one, see how to dockerize an app. - Docker: the engine that runs containers. See what is Docker.
- Image and container: the image is the immutable template, the container is a running instance of it.
- Kubernetes and Swarm: cluster orchestrators for multiple hosts.
FAQs
Is Docker Compose the same as Docker?
No. Docker runs individual containers. Compose sits on top of it and describes several containers, networks and volumes in one file, then manages them together.
What is the Docker Compose file called?
The preferred name is compose.yaml (or compose.yml). For backward compatibility, docker-compose.yaml and docker-compose.yml also work. If both styles exist, Compose uses compose.yaml.
Do I need the version field in compose.yaml?
No. The top-level version property is obsolete, only informative, and triggers a warning if you use it. Compose validates the file against the most recent schema.
Can I use Docker Compose in production?
Yes, on a single server. The official docs cover deploying to a single host or a remote Docker host. For multiple servers, teams usually reach for another orchestrator.
More in Deploy
See all 33 articles- What are environment variables?Environment variables are key-value pairs the operating system hands to a process at startup. Learn to read them in Node.js and Python and keep tokens out of code.
- What is a deployment?A deployment takes a version of your code to a server, prepares it and starts it. See the stages, the methods and how to roll back when it goes wrong.
- How much RAM and CPU does my app need?Size RAM and CPU by measuring your app's real peak and adding about 30% headroom. Learn how to measure in Node.js, Python and Java and what to do if short.
- Automatic deploys with GitHub ActionsAutomatic deploys with GitHub Actions ship your code to the server on every push. See the workflow step by step, secrets, tests before deploy and common errors.