Docker Compose Builder

Docker Compose Builder

Build docker-compose.yml files visually. Add MySQL, PostgreSQL, Redis, MongoDB services. Free online Docker Compose YAML generator

A docker-compose.yml that spins up Postgres, Redis, and your app for local development is 20 lines, and you have written variations of it twenty times. The interesting friction is not "what does the YAML look like" but "what version of compose syntax should I use", "what restart policy is right", "do I need named volumes or bind mounts here", and "how do I parameterize for dev vs CI". This builder generates docker-compose v3 files (the current standard for Compose v2.x runtimes) with sensible defaults for common services, and explains the choices that production users get wrong.

compose v1 vs v2: what changed

  • Docker Compose v1 (Python, `docker-compose`) — deprecated mid-2023. Used `version: "3"` at top of file.
  • Docker Compose v2 (Go, `docker compose` plugin) — current. The top-level `version:` field is now ignored (and warns). All v3.x files work; new files should just omit version.
  • Compose Specification — the open spec docker-compose v2 implements, also adopted by other runtimes (Buildah, Podman Compose, AWS ECS, Kubernetes via Compose Bridge). Write to the spec; let runtimes consume.
  • docker swarm — uses the same YAML format but with deploy keys (replicas, placement). Not the same as Kubernetes; rarely used outside legacy deployments.

If you are starting fresh in 2026: omit the version line, use the spec, target `docker compose` (no hyphen). Old guides that say `version: "3.8"` still work but are unnecessary.

Working example: a typical dev stack

Input

A web app: Node.js service, Postgres 16, Redis 7. Dev mode with hot reload.

Output

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app:cached
      - /app/node_modules
    environment:
      DATABASE_URL: postgres://app:app@db:5432/app
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - cache-data:/data

volumes:
  db-data:
  cache-data:

Notes: `depends_on` with `condition: service_healthy` is the right answer for ordering startup — without the condition, depends_on only waits for the container to start, not for the service inside to accept connections. The `:cached` mount flag is macOS-specific (improves bind-mount performance, ignored elsewhere). `node_modules` is a separate anonymous volume to prevent the host's empty node_modules from shadowing the container's.

Configuration that production users get wrong

  • restart policy — `no` (default), `on-failure`, `unless-stopped`, `always`. For dev: `no` is fine. For production-on-a-VM: `unless-stopped`. For long-running services: `always`. NEVER use `always` if your service has a crash bug — you will infinite-loop restart.
  • Network — Compose creates a default network. For multi-stack setups, define networks explicitly to control isolation. Two compose files on the same host can collide on bridged networks if not isolated.
  • Volumes — `named volume` for data that should survive `docker compose down`; `bind mount` (./data:/data) for code or config you want to edit on the host. NEVER bind-mount node_modules / target/ / .venv from host to container — host platform may not match container platform.
  • depends_on without healthcheck — only waits for container start, not readiness. Postgres accepts connections ~3 seconds after container start; your app crashes during that window. Always use `condition: service_healthy` with a healthcheck on the dependency.
  • env_file vs environment — env_file loads from a .env file (which you should .gitignore). environment hardcodes. For secrets, prefer env_file + .gitignore, or docker secrets for production.

When to reach for this tool

  • You are starting a new project and want a working dev compose file without hunting for examples.
  • You inherited a compose file with `version: "2"` and outdated syntax (`links:`, `expose:`) and want to modernize.
  • You need to add a new service to your stack (Elasticsearch, Kafka, MinIO) and want the minimal config that "just works".
  • You are reviewing a compose file in a PR and want to flag the typical mistakes (depends_on without healthcheck, restart: always on a buggy service).

What this tool will not do

  • It will not provision your servers. Compose runs containers on a single host. For multi-host production, use Kubernetes (and Compose Bridge or kompose to convert), or a managed service (ECS, Cloud Run, Fly.io).
  • It will not handle secrets securely. Compose .env files end up in process environment, which crash dumps and logs can leak. For real secrets in production: docker secrets, HashiCorp Vault, cloud KMS.
  • It will not optimize your Dockerfile. Image size and build speed are separate concerns from compose layout. Pair compose authoring with multi-stage Dockerfiles and BuildKit.

Frequently asked questions

Do I still need `version: "3.8"` at the top?

No. Compose v2 ignores it and prints a warning. Omit it. Compose Specification compatibility is implicit; the runtime picks the latest features your YAML uses.

What is the difference between `expose` and `ports`?

`ports` publishes the port to the host (accessible from outside Docker). `expose` only exposes within the Docker network (accessible from other containers, not from your laptop). For dev where you connect from the host, use `ports`. For service-to-service that should not be reachable externally, use `expose` (or just omit — Docker network connections do not need expose declared in modern compose).

How do I handle different configs for dev vs CI vs production?

Use compose override files. `docker compose up` reads `docker-compose.yml` and `docker-compose.override.yml` automatically. For CI: `docker compose -f docker-compose.yml -f docker-compose.ci.yml up`. Each override file overrides specific services and merges with the base.

Should I commit `.env` to git?

Never. Commit `.env.example` with placeholder values; .gitignore `.env` (which has real values). On a new clone, copy .env.example to .env and fill in values. Required-but-secret values go in a secret manager, not env files.

My containers cannot talk to each other by service name.

Almost always: they are on different networks. Compose default network has DNS for service names, but if you defined custom networks, services must share at least one. Check `docker network inspect <network>` to see what is attached.

How do I run `docker compose` against a swarm or k8s cluster?

For Swarm: `docker stack deploy -c docker-compose.yml mystack`. For Kubernetes: use Compose Bridge (built into Docker Desktop) or `kompose convert -f docker-compose.yml`. Both translate compose syntax to native cluster manifests with varying fidelity.

Related tools

Last updated · E-Utils editorial team