All Tools View Categories Blog About Contact Privacy

How to Create a docker-compose.yml File (With Examples)

How to Create a docker-compose.yml File (With Examples)

A docker-compose.yml file replaces a dozen docker run flags with one declarative file that starts an entire stack — web, database, cache — with a single command: docker compose up -d. Instead of memorizing port mappings, volume mounts, and network flags, the file describes each service, its image, ports, environment, and dependencies in versioned YAML.

This guide explains how to create a docker-compose.yml file from zero, the anatomy of services, volumes, and networks, step-by-step creation, three copy-paste examples, and the pitfalls that cause compose failures.

TL;DR — Quick Answer: Create a docker-compose.yml with a services: block — define each service with image or build, ports: ["host:container"] (quoted), env_file: .env, and volumes for persistence — then run docker compose up -d. Use a docker-compose generator to build the YAML with examples and no syntax memorization.
How to create a docker-compose.yml file with examples - multi-container apps

What Is docker-compose.yml?

docker-compose.yml is a YAML file that defines how Docker Compose should run multiple containers together. Per the Compose Specification, it declares services (containers), volumes (persistent data), and networks (connectivity) in one versioned file that lives alongside the code and is committed to git.

Where a Dockerfile builds one image, a compose file orchestrates many images. Where docker run is imperative (flags typed each time), compose is declarative (desired state in a file). This makes the stack reproducible: docker compose up -d recreates the same network, volumes, and containers on any machine.

Anatomy of docker-compose.yml file - services, volumes and networks

Anatomy of a docker-compose.yml File

Answer-first: 3 top-level keys cover most files — services, volumes, and optional networks. Each service has image/build, ports, env_file, volumes, depends_on.

services:
  web:
    image: myapp:1.0        # or build: .
    ports: ["3000:3000"]   # host:container (quoted!)
    env_file: .env         # env vars from file
    depends_on: [db]       # start order
  db:
    image: postgres:16-alpine
    volumes: [db-data:/var/lib/postgresql/data]
    environment: {POSTGRES_DB: mydb}
volumes:
  db-data: {}              # named volume declared once

Key sections:

  • services → each container: web, db, redis — each with image (pull) or build: . (build from Dockerfile), ports, env_file, environment, volumes, depends_on
  • volumes → persistent data: Named volumes like db-data survive docker compose down (removed only with -v). They are declared top-level once and referenced in services.
  • networks → connectivity: Omitted in minimal files — Compose creates a default bridge where services reach each other at http://service-name:port (e.g., web → db at db:5432). Custom networks are added only when isolation is needed.

YAML rules: Indent with 2 spaces (not tabs), quote "host:container" because of the colon, and note that depends_on controls start order, not readiness — the app must retry or use a healthcheck.

How to Create a docker-compose.yml File — 4 Steps

How to create docker-compose.yml file step-by-step - 4 steps
  1. Define services: Each service is one container. For a web + db + cache stack, define web: image: myapp:1.0, db: image: postgres:16-alpine, and cache: image: redis:alpine. Use build: . if a Dockerfile should be built, or image: nginx to pull.
  2. Map ports and volumes: ports: ["3000:3000"] publishes the container port to the host (quoted). volumes: [db-data:/var/lib/postgresql/data] mounts a named volume for persistence. Without a volume, database data is lost on down.
  3. Set environment: Never hardcode secrets in the YAML. Use env_file: .env (with .env in .gitignore) or environment: {POSTGRES_DB: mydb} for non-sensitive config. Compose loads .env automatically if present.
  4. Bring it up: docker compose up -d builds (if build:), creates the default network and volumes, and starts all services. docker compose ps shows status, docker compose logs -f follows logs, and docker compose down stops and removes the network. Add -v to also remove volumes (caution: deletes data).

Minimal working file for a Node + Postgres app:

services:
  web:
    image: myapp:1.0
    ports: ["3000:3000"]
    env_file: .env
  db:
    image: postgres:16-alpine
    volumes: [db-data:/var/lib/postgresql/data]
volumes: { db-data: {} }

docker-compose.yml Examples — Copy-Paste Ready

docker-compose.yml examples for Node, WordPress and Redis stacks

Full-Stack (Node + Postgres)

services:
  web:
    build: .
    ports: ["3000:3000"]
    env_file: .env
    depends_on: [db]
  db:
    image: postgres:16-alpine
    volumes: [pgdata:/var/lib/postgresql/data]
    environment: {POSTGRES_DB: mydb, POSTGRES_USER: user, POSTGRES_PASSWORD: pass}
volumes: { pgdata: {} }

Web at http://localhost:3000, DB at db:5432 from the web container.

WordPress (WP + MySQL)

services:
  wp:
    image: wordpress:latest
    ports: ["8080:80"]
    environment: {WORDPRESS_DB_HOST: db, WORDPRESS_DB_USER: wpuser, WORDPRESS_DB_PASSWORD: wppass}
  db:
    image: mysql:8
    environment: {MYSQL_ROOT_PASSWORD: secret, MYSQL_DATABASE: wordpress}
    volumes: [dbdata:/var/lib/mysql]
volumes: { dbdata: {} }

WP at http://localhost:8080 — classic CMS stack in one file.

App + Cache (Node + Redis)

services:
  app:
    image: myapp:1.0
    ports: ["3000:3000"]
  cache:
    image: redis:alpine
    ports: ["6379:6379"]
    volumes: [redis:/data]
volumes: { redis: {} }

Add caching in 5 lines — app reaches cache at cache:6379.

Common Mistakes to Avoid

Common docker-compose.yml mistakes to avoid - ports, secrets and depends_on
  • Unquoted ports: ports: 3000:3000 fails — YAML parses 3000:3000 as a number. Always quote: ports: ["3000:3000"].
  • Hardcoded secrets: environment: POSTGRES_PASSWORD: mysecret gets committed. Use env_file: .env and add .env to .gitignore.
  • Assuming depends_on waits: It starts db first, but the app may connect before the DB is ready. Fix with retry logic in the app or a healthcheck plus condition: service_healthy.
  • Using version: '3.8': The Compose Spec no longer requires it — it is ignored. The generator omits it.
  • Misunderstanding down -v: docker compose down keeps volumes; down -v deletes them. The latter is destructive for databases.

Compose vs Dockerfile vs docker run — When to Use Which

ToolScopeUse
DockerfileOne imageBuild a single container type
docker-compose.ymlMany imagesOrchestrate web + db + cache with one command
docker runOne containerManual, imperative flags for single runs

Compose files are declarative and versioned — commit them to git. docker run flags are imperative and easy to mistype.

Using a Generator (No YAML to Memorize)

Select services (web, db, cache) with pre-filled images (postgres:16-alpine, redis:alpine), set ports and env_file, and copy the generated YAML. The generator ensures 2-space indentation, quoted ports, correct nesting, and valid Compose Spec — no YAML memorization. Change services to adapt across stacks; the pattern remains the same.

FAQs About docker-compose.yml

What is docker-compose.yml?

A YAML file that defines services, volumes, and networks for Docker Compose. It lets multiple containers be started with docker compose up -d instead of many docker run commands.

How do I run a docker-compose.yml file?

Place the file in the project root and run docker compose up -d to start, docker compose ps for status, docker compose logs -f for logs, and docker compose down to stop.

What is the difference between Dockerfile and docker-compose.yml?

A Dockerfile builds one image; a compose file orchestrates multiple images (services) plus their ports, volumes, and networks. The Dockerfile defines a container type; compose defines the stack.

Do I need to create networks and volumes manually?

No — Compose creates a default bridge network and named volumes automatically when declared. Services reach each other at http://service-name:port (e.g., db:5432).

How do I use environment variables in compose?

Use env_file: .env to load from a file (add .env to .gitignore) or environment: {KEY: value} for non-sensitive config. Never commit secrets in the YAML.

Why is my compose file failing to parse?

Common causes are unquoted ports ("3000:3000" required), tabs instead of 2-space indentation, and a missing services: key. A generator produces valid YAML that avoids these.

Conclusion

A docker-compose.yml file turns a fragile set of docker run flags into a reproducible, versioned stack. Define services, map ports and volumes, load environment from .env, and bring the whole app up with one command. When the next service is added, the pattern repeats without relearning flags.

Generate the next compose file with a visual builder — select services, set ports and env, and copy a correct YAML file in seconds.