What is Docker Compose?

Docker Compose is a tool that defines a multi-container application in a single YAML file. One command, `docker compose up`, creates the networks and volumes and starts every service.

Updated 4 min read

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
On this page

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.

compose.yamlservices:web:db:cache:volumes:db-data:docker compose upshared networkwebyour imagedbPostgreSQLcacheRedisdb-datavolume
compose.yaml describes it all; docker compose up creates the services, the shared network and the database volume.

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.

yaml
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 the Dockerfile in the folder. image: postgres:18 uses 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 db as the host, not localhost.
  • ${DB_PASSWORD} is read from your environment or from a .env file in the same folder, so the password is not written in the YAML. See what a .env file is.
  • The db-data volume keeps the database files even if the container is recreated.
  • $$ in the healthcheck escapes 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:

bash
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 network

down 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:

  1. Development environments: the database, cache, queue and APIs your project depends on start with one command, identical for the whole team.
  2. Automated testing: a CI pipeline creates an isolated environment, runs the suite and tears everything down.
  3. 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 build but 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.

Next articleHow to dockerize an app (your first Dockerfile)Dockerizing means packaging your app and its dependencies into an image. Learn to write a Dockerfile for Node.js, build it, run it and fix common errors.

More in Deploy

See all 33 articles