Docker

What Is Docker Compose? Simplifying Multi-Container Apps

What Is Docker Compose? Simplifying Multi-Container Apps

Run a web app and a database with plain docker run and you end up juggling two long commands, each with its own flags, and you have to remember which one goes first. Docker Compose swaps that for one YAML file and one command that starts everything.

Docker, Inc. maintains it, and it implements the open source Compose Specification. Developers use it to declare services, networks and volumes for local development, CI pipelines and single-server production, usually when a full Kubernetes cluster would be overkill.

The GitHub repository has more than 37,000 stars and 5,800 forks, which is a lot for a container tool (GitHub, 2026).

What Is Docker Compose

YouTube player

You list each service once in a YAML file, then one instruction starts the whole stack. No separate docker run command for every service.

It sits on top of what Docker already does and calls the same engine and CLI that run ordinary containers. It doesn’t replace containerization itself. Its job is to coordinate containers that were built independently.

Scope is the real difference. A Dockerfile builds one image and docker run starts one container, but Compose starts, stops and rebuilds an entire multi-container application together.

A few numbers help with context. The Compose Specification, the current file format standard, is implemented by Compose 1.27.0 and later (Docker Docs, 2026). Docker ended support for the original Python based Compose V1 at the end of June 2023, and it no longer ships with Docker Desktop (Docker, 2023).

Adoption is high too. 71% of developers who work with containers use Docker Compose, making it the single most used container tool in Docker’s 2024 State of Application Development Report.

What Docker Compose Is Not

People mix it up with tools that solve a different problem, so a quick clarification helps.

It isn’t a container runtime, since it still calls Docker Engine to run anything. It also isn’t a multi host orchestrator like Kubernetes or Docker Swarm, because one Compose project stays on a single Docker host.

Why has Docker revolutionized deployment?

Explore Docker statistics: containerization adoption, DevOps transformation, enterprise usage, and how containers changed software delivery.

Discover Docker Insights →

And it isn’t a build tool on its own. The build key can trigger a Dockerfile build when no image exists yet, but that’s as far as it goes.

What Is a docker-compose.yml File

YouTube player

Every service, network and volume for a project gets declared in one YAML document. Docker reads it top to bottom and turns each block into a running piece of the application.

The top level keys break down like this.

KeyPurposeTypical value
servicesdefines each container in the stackweb, db, cache
networksdeclares custom networks between servicesfrontend, backend
volumesdeclares named volumes for persistent datadb-data

Older projects use the name docker-compose.yml. The Compose Specification names compose.yaml as the preferred default filename and keeps docker-compose.yml supported for backward compatibility.

If the indentation rules are new to you, skim a quick YAML syntax reference before writing a first file. One misplaced space breaks the whole block, and the error message rarely points at the real culprit.

Inside each service you’ll mostly touch a handful of fields. The image field points to a prebuilt container image, often pulled from a registry such as Docker Hub. The build field points to a Dockerfile and its build context when there’s no ready image. Ports maps a container port to a host port, and environment sets variables inside the container. Then there’s depends\_on, which controls startup order between services.

A minimal file usually pairs a web service with a database service, linked through depends\_on and a shared network.

Docker Compose vs Dockerfile

Most projects use both, even though they solve different problems.

The Dockerfile is the recipe for one container image. It lists the base image, the files to copy and the commands to run at build time. Compose takes those images (or the Dockerfiles that produce them) and wires the resulting containers into a working application.

ToolDefinesScopeTypical command
Dockerfileone imagesingle builddocker build
docker runone containersingle containerdocker run
Docker Composea full applicationmultiple servicesdocker compose up

The build key inside a service definition points straight at a Dockerfile, so Compose can build an image on the fly instead of pulling one.

Apps split into separate services under a microservices architecture lean on this the most, since each service usually carries its own Dockerfile.

35% of developers now work on microservices based applications, far more than those working on monolithic or hybrid monolith and microservices applications (24% each), though still behind client-server applications at 38%, according to Docker’s 2025 State of Application Development Report.

That’s why a tool built to start several Dockerfile built containers together earns a spot next to Docker itself.

Core Docker Compose Commands

YouTube player

Day to day, a small set of commands covers almost everything.

  1. docker compose up starts every service defined in the file, building images first if needed
  2. docker compose down stops and removes the containers and the default network Compose created, while leaving named volumes in place
  3. docker compose build rebuilds images without starting the containers
  4. docker compose ps lists the running services and their status
  5. docker compose logs streams output from one service or all of them
  6. docker compose exec opens a shell inside a running container for debugging

Add -d to docker compose up and the process detaches, so the terminal stays free. Adding –build forces a rebuild before start, and –remove-orphans clears out containers that are no longer listed in the file.

Keep a full Docker command reference open the first few times you mix and match these flags. I still do, honestly.

How Docker Compose Handles Networking

YouTube player

Every Compose project gets its own network the moment it starts.

Docker creates a default bridge network named <project-name>\_default. The project name comes from the project folder unless you override it, and every service in the file joins the network automatically.

Services reach each other by service name instead of an IP address. Containers from one project stay isolated from containers in another. A port only reaches the host machine when the ports key says so, and all of this applies to every service by default unless you declare a custom network instead.

A backend service can connect to db:5432 directly, for example through a PostgreSQL connection string, as long as db is the name you gave the database service in the file. No manual network setup needed.

Custom networks, declared under the top level networks key, let a team split a stack into segments. A public facing service goes on one network and an internal database on another.

Ports left undeclared stay reachable only inside that internal network, never from outside the host.

How Docker Compose Handles Volumes and Data Persistence

Containers are disposable by default. Data written inside one disappears the moment that container gets removed.

Compose deals with this through two options, both declared per service under the volumes key.

ApproachProsCons
Named volumemanaged by Docker, easier to back up and migrate than a bind mount, survives docker compose downless visible on the host filesystem
Bind mountmaps straight to a host folder, ideal for live code editingties the setup to that machine’s folder structure

Named volumes get declared once under the top level volumes key, then referenced by name inside a service. Bind mounts skip that step and just list a host path next to a container path.

That’s why most local development setups use bind mounts for source code.

A database almost always gets a named volume instead. Losing its data on every restart would defeat the point of running it.

How Docker Compose Reads Environment Variables

YouTube player

Configuration values rarely belong inside an image, so Compose pulls them in at runtime.

An .env file sitting next to the compose file gets read automatically, and it feeds variable substitution inside the YAML itself. The environment key lists variables directly on a service. The env\_file key points to a separate file of variables to load into that one service, which is handy when only one container needs them.

Substitution uses the ${VARIABLE} syntax anywhere in the file. One .env file can drive image tags, ports or credentials across every service at once.

Compose follows two separate precedence rules. For the final value inside a container, a variable passed with docker compose run -e wins first, then the environment key, then env\_file, and last any ENV value baked into the image. For ${VARIABLE} substitution inside the YAML itself, values from the shell environment win over the .env file.

Teams that skip the .env file usually end up hardcoding passwords straight into docker-compose.yml. Avoid that, even for a throwaway local setup.

Compose Specification and Version History

YouTube player

The file format has gone through several eras since Compose first shipped.

FormatStatusMinimum Docker Engine
Version 1legacy, no version key required1.9.0
Version 2.x / 3.xlegacy, still readablevaries by minor version
Compose Specificationcurrent standard19.03.0 or later

The Compose Specification merged the old 2.x and 3.x branches into a single schema, which made the version key optional.

Compose V2 replaced the original Python based tool with a Go binary built straight into the docker CLI. Compose v5, released in 2025, is functionally identical to V2 and adds an official Go SDK (Docker Docs, 2026).

Docker Desktop 3.4 and later installs Compose V2 automatically. Docker Desktop 4.4.2 and later goes further and aliases the old docker-compose syntax to docker compose by default.

The command itself changed shape along the way, from a hyphenated docker-compose binary to a spaced docker compose command that runs as a CLI plugin.

It wasn’t just cosmetic. GitHub announced that it would remove Compose v1 from its hosted runner images on July 9, 2024, then postponed the rollout to July 29, 2024, forcing any CI workflow still typing docker-compose to switch or start failing (GitHub, 2024).

When to Use Docker Compose Instead of Plain Docker Commands

YouTube player

A single container project rarely needs Compose. The moment a second service joins, typing out docker run flags for each one gets tedious fast.

Compose wins when the app needs two or more services that talk to each other, when new team members need an identical setup without memorizing flags, or when the whole stack has to start, stop or rebuild as one unit.

Plain docker commands still make sense for a single container with no dependencies, or for a one off test image that gets thrown away right after.

58% of developers report intermediate to advanced familiarity with Docker Compose, according to JetBrains’ State of Developer Ecosystem survey, 2023.

That familiarity explains why teams reach for it once a project outgrows a single container, instead of scripting docker run by hand.

A 2016 merge request to GitLab’s own developer environment project, the GitLab Development Kit, proposed a docker compose file so contributors could bring up the development environment without installing every dependency by hand on their own machines (GitLab.org, 2016).

Docker Compose vs Kubernetes

YouTube player

Both start multiple containers, but they work at very different scales.

Compose runs everything on one Docker host. Kubernetes spreads containers across a cluster of machines and restarts or reschedules them automatically when one fails.

FactorDocker ComposeKubernetes
Host scopesingle hostmulti host cluster
Self healingrestart policies on the same host onlyautomatic rescheduling
Setup timeminuteshours to days
Best fitlocal dev, small productionlarge scale, high availability

82% of container users now run Kubernetes in production, up from 66% in 2023, according to the CNCF Annual Cloud Native Survey, published January 2026.

That growth tracks workloads that outgrew a single server, not the small projects Compose was built for.

Moving between the two doesn’t mean a full rewrite. Docker’s Compose Bridge feature converts a compose.yaml file into Kubernetes manifests, generating deployments, services and persistent volume claims straight from the service definitions you already wrote.

Is Docker Compose Suitable for Production

YouTube player

Yes, for the right kind of deployment.

Docker’s own documentation describes running a Compose defined application directly in production, either on a single server or scaled up on a Swarm cluster.

A few things make a setup production ready. Restart policies such as unless-stopped bring containers back after a crash or reboot. Healthcheck definitions let Compose tell a broken service from a healthy one. A separate override file keeps local bind mounts and debug flags out of the deployed version, and environment variables get swapped for production values like quieter logging and real service endpoints.

The open source Symfony Docker starter project ships a Compose configuration built for single server production, kept separate from its development file.

What Compose won’t do by itself is scale a service across multiple physical machines, or reschedule a container onto a healthy server when one goes down.

When Docker Compose Does Not Apply

YouTube player

It’s not the right tool for every job.

Skip it when an application has to run across more than one physical or virtual host at the same time. Skip it when you need automatic failover if a server goes down, since Compose has no built in rescheduling. The same goes for a service that has to scale up and down based on load, for a platform team that has already standardized on Kubernetes manifests and tooling, and for compliance rules that demand fine grained resource quotas and scheduling controls across a shared cluster.

None of this makes Compose a lesser tool. It just wasn’t built for cluster level problems.

Reaching for Kubernetes, Docker Swarm, or a managed container service early, rather than stretching a single Compose file past its limits, saves a rewrite later.

How to Write and Run a Basic Docker Compose File

A two service stack takes a handful of steps.

  1. Create a project folder and add a file named docker-compose.yml or compose.yaml inside it
  2. Add a services key, then define a web service with an image or a build path
  3. Add a second service, such as a database, with its own image and a named volume for persistence
  4. Connect the two with depends\_on, so the database starts before the web service
  5. Run docker compose up -d from that folder to build, create and start everything
  6. Check status with docker compose ps and stream logs with docker compose logs -f
  7. Stop and remove everything with docker compose down when finished

Compose reads the file relative to the folder it runs from, so run the command from that same directory or point to the file with the -f flag.

Leaving off -d keeps the logs streaming in the same terminal window, which is useful the first time a new stack gets tested.

Common Docker Compose Errors and Fixes

A handful of errors cause most Compose problems.

ErrorCommon causeFix
Port is already allocatedanother process or container already uses that host portchange the host side of the ports mapping, or stop the other process
Unsupported config optiona v3 only key used under the Compose Specification, or the reversecheck the key against the current Compose Specification reference
Container name already in usea fixed container\_name colliding with a leftover container from a previous runrun docker compose down first, or drop the fixed container\_name
Variable is not set, defaulting to blank stringa referenced variable is missing from the .env file or the shelladd the variable to .env, or export it before running compose up

Version mismatches show up when a compose.yaml written for the Compose Specification gets run against an old Compose V1 binary that never learned that syntax.

Upgrading to Compose V2, which ships with Docker Desktop and installs as a plugin alongside Docker Engine, clears most of these in one step.

FAQ on What Is Docker Compose

Where Does the Name Docker Compose Come From

It describes what the tool does. It composes multiple containers, defined as services in a docker-compose.yml file, into one running application, so you don’t start each container by hand with its own docker run command.

Is Docker Compose Free to Use

Yes. Docker Compose is open source software under the Apache 2.0 license, with no license cost for the tool itself. On Linux it costs nothing beyond the compute the containers consume. On macOS and Windows it usually arrives through Docker Desktop, which is free for personal use, education, non-commercial open source, and businesses with fewer than 250 employees and less than $10 million in annual revenue, but requires a paid subscription for larger organizations.

Do I Need Docker Desktop to Use Docker Compose

No. Docker Compose runs as a CLI plugin installed alongside Docker Engine on Linux servers with no Docker Desktop installed. Desktop adds a graphical interface, and the Linux virtual machine that containers need on macOS and Windows, while the underlying docker compose commands stay identical everywhere.

How Do I Install Docker Compose

Docker Desktop bundles Compose automatically on macOS and Windows. On Linux, install the docker-compose-plugin package through your distribution’s package manager or Docker’s official install script, then confirm it worked with docker compose version. No separate download required.

Can Docker Compose Scale Services Across Multiple Hosts

No. Docker Compose manages containers on a single Docker host only. The –scale flag can run more replicas of one service, but every replica still lands on that same machine. True multi-host scaling needs Docker Swarm or Kubernetes.

What Changed Between docker-compose and docker compose Command Syntax

The flags and compose file keys stayed largely the same. The main change is the invocation, from a separate docker-compose binary to a docker compose subcommand built into the Docker CLI. Older scripts calling the hyphenated form can still work through aliasing.

What Should You Do First With Docker Compose?

Compose earns its place the moment a local project needs more than one container. So write a docker-compose.yml file for that project first, rather than reaching for Kubernetes or Docker Swarm on day one.

Use the one file for local development, reuse it inside a continuous integration pipeline, and move to Kubernetes only once workloads outgrow a single host.

The order matters. Rewriting a working Compose file for Kubernetes before you need to adds cluster configuration a small team has no use for yet.

The two figures come from different surveys and different populations, but 58% familiarity with Docker Compose next to 82% production Kubernetes adoption among container users fits a pattern of teams learning Compose first, then moving specific workloads to a cluster once one host is not enough.

Getting the install right the first time prevents most early setup problems, so setting up Docker Compose on a new machine is the logical next step.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.