What Is Docker Compose? The Beginner's Guide (The compose.yaml Mental Model, Services vs Images, and the v2 Rules)
CodexFuse Team · 2026-11-03T14:49:41.387Z · 14 min read min read
What Is Docker Compose? The Beginner's Guide (The compose.yaml Mental Model, Services vs Images, and the v2 Rules)
Learning what Docker Compose is changes how you think about running software: instead of starting one container at a time with long docker run commands, you describe your whole application — the app, the database, the cache, their networks, their storage — in a single YAML file, and launch the entire stack with one command. Docker Compose is Docker's official tool for defining and running multi-container applications, and the mental model is deliberately simple: one file, one command, whole stack. This beginner's guide covers the compose.yaml mental model, how services differ from images and containers, Docker Compose vs Dockerfile, the v1-vs-v2 naming rules, the everyday command set, and the five mistakes beginners make most. If you have never run Docker at all, start with our Docker beginner guide — this page builds on it.
What Is Docker Compose, Exactly?
Docker Compose is a tool for defining and running multi-container applications, described in Docker's own documentation as the way to control "your entire application stack, making it easy to manage services, networks, and volumes in a single YAML configuration file" — and then, "with a single command, you create and start all the services." That is the whole definition: Compose turns a description of your application (the YAML file) into a running set of containers (the stack), and it can tear the stack down just as cleanly.
Here is the compose.yaml mental model to memorize: a Compose file describes services, not containers. Each service is a recipe — which image to use (or which Dockerfile to build), which ports to open, which environment variables to pass, which volumes to mount, which network to join. Compose reads your services, resolves their relationships (this service needs that database first), creates a private network, and starts each service as a container. You never touch the individual containers directly; you operate on the stack, and Compose figures out the container choreography.
The official landing page (docs.docker.com/compose, fetched November 3, 2026) frames it in one sentence: Compose "simplifies the control of your entire application stack." If Docker is the engine that runs one container, Docker Compose is the conductor that runs the whole orchestra.
How Does Docker Compose Work? The compose.yaml Mental Model
Docker Compose works by reading a YAML file — canonically named compose.yaml — that declares your stack's services, and optionally networks, volumes, configs, and secrets at the top level. (Per the Compose Specification, services is the only required top-level element: "A Compose file must declare a services top-level element as a map.") Everything else is optional plumbing you add as your stack grows.
A minimal but real docker compose yaml file looks like this:
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgres://db:5432/app
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
volumes:
pgdata:
Read it top to bottom and you can narrate the whole application: a web service built from the current directory, reachable on port 8000, wired to a db service that starts only once its health check passes, with database files persisted in a named volume. That narration is the mental model — the file is the documentation of your stack. A few key service fields, verified against the official services reference (docs.docker.com/compose/compose-file/05-services, fetched November 3, 2026):
| Field | What it does |
|---|---|
image | Starts the service from a published image, e.g. postgres:16 |
build | Builds the service's image from source (a Dockerfile) instead of pulling one |
ports | Maps host ports to container ports — "8000:8000" is host:container |
environment / env_file | Injects environment variables; env_file reads them from a file, and environment values take precedence over env_file |
volumes | Mounts storage into the container — the only way data survives a container restart |
depends_on | Controls startup and shutdown order; conditions include service_started, service_healthy, and service_completed_successfully |
restart | Policy applied when a container terminates — e.g. unless-stopped for a web service |
Compose auto-creates a default private network for your stack, so services reach each other by service name — the web service connects to db simply by using the hostname db. No IP addresses to memorize, no manual network flags. This is the convenience docker run users notice first: networking that just works.
Services vs Images vs Containers: The One Picture to Memorize
The beginner confusion around docker compose explained usually comes down to three words — service, image, container — used as if they were interchangeable. They are not, and the distinction is the whole game:
- An image is a frozen blueprint. It is built once (from a Dockerfile or pulled from a registry) and never changes while it runs.
- A container is a running instance of an image. Start it, stop it, delete it — the image underneath is untouched.
- A service is the Compose-level recipe for running containers from an image. It says which image to use, what ports to open, what environment to inject, and how it relates to the other services. One service can even scale to several containers of the same recipe.
Think of it as: the image is the cake recipe card, the container is the actual cake in the oven, and the service is the bakery order form that says "bake three of these, ice them blue, deliver them together." Compose is the bakery: you hand it the order form (compose.yaml) and it bakes, ices, and delivers.
Docker Compose vs Dockerfile: What's the Difference?
The short answer: a Dockerfile builds one image; Docker Compose orchestrates a multi-container stack. They are not competitors — they are different layers of the same workflow, and most real projects use both.
| Dockerfile | Docker Compose | |
|---|---|---|
| Answers the question | "How do I build this one image?" | "How do I run this whole application?" |
| Input | A text document of build instructions (Docker's definition: "all the commands a user could call on the command line to assemble an image") | A YAML file describing services, networks, and volumes |
| Output | One image | A running stack of containers |
| Typical command | docker build | docker compose up |
| When you need it | When you have code to package into an image | When your app has two or more moving parts (app + database + cache) |
The relationship is direct: a Compose service's build: . field points at a Dockerfile, so Compose itself runs the build and then starts the stack. A single-container app never needs Compose; a three-container app with hand-typed docker run commands is a bug waiting for a restart. If you want the runtime fundamentals first, our guide to Docker fundamentals covers images and containers before you add orchestration.
Why Is It docker compose With a Space? The v1-vs-v2 Rule
It is docker compose with a space because docker-compose with a hyphen is the old version — Compose v1 — which Docker retired: per Docker's own announcement (January 31, 2023), "Compose V1 support will no longer be provided after June 2023," and the current migration page (updated September 3, 2026) states plainly that v1 is the Python-based tool "superseded by Compose v2," written in Go and integrated into the Docker CLI, and is "no longer maintained — migrate to v2."
The practical rules for 2026:
- Type
docker compose(space), neverdocker-compose(hyphen). If an old tutorial shows the hyphen, mentally convert it; if your system has no v2 plugin, update Docker, not your habits. - You do not need a
version:field. Compose v2 implements the Compose Specification directly and ignores version numbers — leave the field out entirely. - Keep the file minimal. The spec defaults are sane; every line in your compose.yaml should earn its place.
This is the single most common beginner stumbling block in docker compose tutorials: half the internet still shows the hyphen. When a command fails with "command not found," the cause is almost always the old binary.
compose.yaml vs docker-compose.yml: Which Name Should You Use?
Use compose.yaml — it is the canonical name in the official documentation (the Compose quickstart creates compose.yaml, and the CLI reference describes auto-lookup as traversing the working directory "looking for a compose.yaml or docker-compose.yaml file").
In practice, docker-compose.yml is still honored widely for compatibility (the 2026 Linuxize Compose guide documents it as supported), so an old file with that name will keep working. But new files should use compose.yaml: it is what the docs write, what new tooling expects, and what future-you will thank you for. If Compose "can't find the file," check you are running the command from the directory containing it — or pass -f with the exact path.
What Are the Docker Compose Commands? The Everyday Set
These eleven commands cover roughly everything a beginner does day to day, with their one-line behavior from the official CLI reference (docs.docker.com/reference/cli/docker/compose, fetched November 3, 2026):
| Command | What it does | When you use it |
|---|---|---|
docker compose up | Create and start containers | First launch and every relaunch |
docker compose up -d | Same, detached (background) | Running the stack while you work |
docker compose down | Stop and remove containers and networks | Full teardown; add -v to also remove named volumes |
docker compose ps | List containers | "What is running right now?" |
docker compose logs | View output from containers | Debugging; -f follows live |
docker compose exec | Execute a command in a running container | exec web bash opens a shell inside |
docker compose stop / start | Stop / start services without removing them | Pausing work; keeps volumes and config intact |
docker compose restart | Restart service containers | Picking up config changes that don't need a rebuild |
docker compose pull | Pull service images | Refreshing published images before up |
docker compose build | Build or rebuild services | After changing code or the Dockerfile |
docker compose config | Parse, resolve and render the compose file in canonical form | Verifying your YAML before up |
docker compose watch | Watch build context and rebuild/refresh containers on file changes | Local development with live reload |
Two beginners' notes: down removes containers and networks but keeps named volumes by default — your database data survives a down unless you add -v. And exec needs the service already running (it runs inside a container); if you need a one-off command without starting the stack, that is what docker compose run is for.
What Does docker compose up Actually Do?
Docker compose up explained in one sentence: docker compose up creates and starts all the containers your stack needs, in the right order, on a private network. The full sequence, in order, is:
- Parse. Compose reads
compose.yaml, resolves variables andenv_fileentries, and renders the final configuration (the same outputdocker compose configshows you). - Build (if needed). Services with a
buildfield get their images built through BuildKit; services with animagefield get pulled if missing. - Create networks and volumes. The stack's private network is created, named volumes are created if they do not exist.
- Order.
depends_onrelationships are resolved — in our example, the database waits for its health check before the web service starts. - Start. Each service becomes a running container, ports are bound, and logs stream to your terminal (or to the background with
-d).
The reverse is exactly as tidy: docker compose down stops and removes the containers and the network, leaving your named volumes — and therefore your data — untouched. If you manage stacks visually instead of in the terminal, our Portainer beginner guide covers the GUI lane for the same stacks.
When Should You Not Use Docker Compose?
Do not use Docker Compose when you have a single container — docker run is simpler and adds nothing to maintain. Do not use it as a production orchestrator at real scale: Compose is a single-host tool, so when you need multiple machines, rolling updates with zero downtime, or autoscaling, that is the domain of Kubernetes or a managed orchestrator, not a YAML file.
Also be honest about what Compose does not do: it does not build images by itself (the Dockerfile does), it does not replace a CI/CD pipeline, and it does not secure your services — a compose.yaml with database passwords in plain environment values is fine on your laptop and a liability on a server. For a first real deployment target, read our what is VPS hosting explainer before putting a Compose stack on a public box.
Docker Compose v5: What's New in 2026?
Compose v5 is the current major line — the releases page for docker/compose showed v5.6.1 as the latest release when checked on November 3, 2026. For a beginner, three facts about v5 matter:
- It is the Go rewrite, matured. v2's move from Python to a Go plugin inside the Docker CLI (the "no longer maintained" v1 migration) is now the settled, only-supported path.
- Compose as a library is real. The v5 release notes carry a "Heads-up for users of Compose as a library" section documenting the
api.ComposeGo API — tooling (like Portainer-style managers) can embed Compose as code, not just shell out to the CLI. - Builds run on BuildKit machinery. Release changelogs track
moby/buildkitupgrades and route build work through bake machinery (progress handling, parallel limits) — which is whydocker compose buildkeeps getting faster without new flags to learn.
One caution from the research: no official v5.0.0 release date or standalone Python SDK was verifiable in the pages fetched this run, so treat any source quoting either with skepticism — the documented facts are the Go API, the BuildKit builds, and the v2-only command line. Prefer the daemonless angle instead? Our Podman beginner guide covers Docker's main alternative and its own compose-compatible workflow.
5 Docker Compose Mistakes Beginners Make
The same five errors appear in nearly every beginner's first week — all avoidable once you know the mental model:
| Mistake | Why it hurts | The fix |
|---|---|---|
Typing docker-compose with a hyphen | v1 is end-of-life; the command may not exist on a modern install | Always docker compose (space) |
Keeping a version: field | Harmless but misleading — v2 ignores it and old tutorials treat it as load-bearing | Delete the field; the spec doesn't need it |
| Storing data inside the container | down deletes containers; data written outside a volume vanishes with them | Mount a named volume for every database and upload directory |
Hard-coding secrets in environment | Passwords end up in version control and shell history | Use env_file or real secrets management, and .gitignore the file |
Running down -v casually | -v deletes named volumes — your database is gone, not paused | Use plain down; reach for -v only when you truly want a fresh database |
Sources
- Docker Docs — "Docker Compose" landing (definition, one-command stack): https://docs.docker.com/compose/
- Docker Docs — "Compose file reference" (Compose Specification; services/networks/volumes; v2 implements it): https://docs.docker.com/compose/compose-file/
- Docker Docs — "Define services in Docker Compose" (services top-level element; build/image/ports/environment/env_file/volumes/depends_on/restart fields): https://docs.docker.com/compose/compose-file/05-services/
- Docker Docs — "docker compose" CLI reference (command list and one-line behaviors; compose.yaml/docker-compose.yaml auto-lookup): https://docs.docker.com/reference/cli/docker/compose/
- Docker Docs — "Docker Compose Quickstart" (compose.yaml canonical example, named volumes, healthcheck, watch, .env, up/down): https://docs.docker.com/compose/gettingstarted/
- Docker Docs — "Deprecated and retired Docker products and features" / migration page (v1 Python-based superseded by Go v2; no longer maintained): https://docs.docker.com/compose/migrate/
- Docker Blog — "Docker Compose: What's New, What's Changing, What's Next" (January 31, 2023; v1 support ends after June 2023): https://www.docker.com/blog/new-docker-compose-v2-and-v1-deprecation/
- Docker Docs — "Dockerfile reference" (Dockerfile as the text document of image-build commands): https://docs.docker.com/reference/dockerfile/
- GitHub — docker/compose releases (v5 line; v5.6.1 latest at check; api.Compose Go API; BuildKit/bake machinery): https://github.com/docker/compose/releases
- Linuxize — "Docker Compose: Define and Run Multi-Container Apps" (2026 guide; compose.yaml preferred, docker-compose.yml still supported; v2 needs no version field): https://linuxize.com/post/docker-compose/
- TheServerSide — "Docker run vs Docker Compose" (hyphen v1 vs space v2; declarative config): https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/Docker-run-vs-docker-compose-Whats-the-difference
Frequently Asked Questions
Is Docker Compose the same as Docker?
No. Docker runs individual containers; Docker Compose defines and runs multi-container applications from a single YAML file. Compose is part of the Docker ecosystem — it needs the Docker engine underneath — but it solves a different problem: orchestrating a whole stack (app + database + cache) instead of one container at a time.
Do I need Docker Compose if I only have one container?
No. A single container is simpler with docker run, and adding a compose.yaml would be pure overhead. Compose earns its place at two or more services, when startup order, shared networks, and volumes start being painful to manage by hand.
Should the file be named compose.yaml or docker-compose.yml?
Use compose.yaml — it is the canonical name in the official documentation, and the CLI auto-discovers compose.yaml (and docker-compose.yaml) by walking up the directory tree. docker-compose.yml remains widely supported for compatibility, so old files keep working, but new files should use the canonical name.
What is the difference between docker compose up and docker compose start?
up creates and starts the stack from scratch — building images, creating networks and volumes, and honoring depends_on order. start only starts services that already exist (after a stop), without recreating anything. Use up for first launches, start/stop for pausing and resuming.
Does docker compose down delete my database data?
Not by default. down stops and removes containers and networks but keeps named volumes, so data in a volume like pgdata survives. Adding -v (docker compose down -v) deletes the named volumes too — which wipes the database. Use -v only when you deliberately want a fresh start.

