DevOps

Docker Compose for Full-Stack Projects: Our Production Template

The annotated Docker Compose template we copy into every full-stack project — web, API, Postgres, and Redis with healthchecks, volumes, and sane defaults.

TTaimoorAI & Backend Engineer
9 min read
D

A good Docker Compose file is the fastest onboarding tool a team can have: clone, run one command, and the whole stack is alive. Here is the template we copy into nearly every full-stack project, with the reasoning behind each choice.

The template

services:
  api:
    build: ./api
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
    ports: ["8000:8000"]

  web:
    build: ./web
    depends_on: [api]
    ports: ["3000:3000"]

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes: [pgdata:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

  cache:
    image: redis:7-alpine
    volumes: [redisdata:/data]

volumes:
  pgdata:
  redisdata:

Healthchecks over guesswork

The most common Compose bug is the API starting before the database is ready. depends_on alone only waits for the container to start, not to be ready. Pairing it with a healthcheck and condition: service_healthy eliminates the race entirely.

Named volumes for persistence

Bind mounts are great for source code in development, but database data belongs in named volumes. They survive docker compose down, are easy to back up, and keep your host filesystem clean.

One .env, never secrets in the file

Keep configuration in a .env file that is git-ignored, and reference variables with ${VAR}. Never commit credentials into the compose file itself — a rule that overlaps with our broader take on access control and security.

Dev and prod from one base

Use a base compose.yaml and override it with compose.override.yaml for local hot-reload, or a compose.prod.yaml for production builds. You keep one source of truth and layer environment-specific changes on top.

Key takeaways

  • Use healthchecks + conditions to kill startup races.
  • Persist data in named volumes, not bind mounts.
  • Keep secrets in a git-ignored .env.
  • Layer dev/prod with override files.

Reliable infrastructure is the backbone of our Cloud & DevOps services. Need a containerization or CI/CD setup that just works? Reach out.

All articlesWritten by Taimoor · AI & Backend Engineer

Have a project that needs this kind of engineering?

We turn ideas into production software — AI, web, mobile, and cloud.

Start a Conversation