Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Cloud & DevOps Fundamentals

Docker Fundamentals

  • Docker Fundamentals
  • Multi-Stage Builds
  • Networking & Storage
  • docker-compose for Local Development
  • Health Checks & Production Best Practices

Kubernetes Essentials

  • Core Objects
  • Services & Ingress
  • Config & Secrets
  • Probes & Autoscaling
  • Deployments & Rollouts

AWS Cloud Fundamentals

  • EC2
  • S3
  • RDS
  • IAM
  • VPC Basics

DevOps Practices

  • CI/CD Pipeline Design
  • Deployment Strategies
  • Infrastructure as Code Awareness
  • Monitoring in Production
  • Cost Awareness
Chaturmind
← Cloud & DevOps Fundamentals

Docker Fundamentals

  • Docker Fundamentals
  • Multi-Stage Builds
  • Networking & Storage
  • docker-compose for Local Development
  • Health Checks & Production Best Practices

Kubernetes Essentials

  • Core Objects
  • Services & Ingress
  • Config & Secrets
  • Probes & Autoscaling
  • Deployments & Rollouts

AWS Cloud Fundamentals

  • EC2
  • S3
  • RDS
  • IAM
  • VPC Basics

DevOps Practices

  • CI/CD Pipeline Design
  • Deployment Strategies
  • Infrastructure as Code Awareness
  • Monitoring in Production
  • Cost Awareness
HomeLearnDevOpsCloud & DevOps FundamentalsDocker Fundamentals
✓ FreeIntermediate· 7 min read

docker-compose for Local Development

Defining a full multi-container dev stack (app + Postgres + Redis + Kafka) in one file, and the important gap between depends_on's startup ORDER and a dependency actually being READY.

Published September 23, 2026


docker-compose for Local Development

Defining a multi-container stack in one file

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=local
      - DB_HOST=postgres
    depends_on:
      - postgres
      - redis
  postgres:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=devpassword
    volumes:
      - pgdata:/var/lib/postgresql/data
  redis:
    image: redis:7-alpine
  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper

volumes:
  pgdata:

One docker-compose.yml file describes an entire local development environment — the application plus every infrastructure dependency it needs (a database, a cache, a message broker) — as a single unit, started together with one command (docker-compose up). This is what replaces the historically painful "install Postgres, install Redis, install Kafka, configure each one locally, hope versions match production" onboarding process with a single, versioned, reproducible file every developer on the team runs identically.

Networking: services reach each other by name, automatically

As covered in Networking & Storage, compose automatically creates a shared network for all services in the file — app reaching postgres simply uses the hostname postgres (the service name), with compose's built-in DNS resolving it, no manual network configuration required at all.

depends_on's real meaning — and its real gap

app:
  depends_on:
    - postgres   # guarantees postgres CONTAINER STARTS before app container starts
                 # does NOT guarantee postgres is actually ACCEPTING CONNECTIONS yet

This is the single most important, most commonly misunderstood point about depends_on: it controls STARTUP ORDER (the postgres container process begins before the app container process begins) — it does not wait for postgres to actually be ready to accept connections. A database container can take several seconds after its process starts before it's genuinely ready to serve queries (initializing its data directory, running startup migrations) — an app container that assumes depends_on means "ready" will frequently fail its first connection attempt in local development, a confusing, easy-to-misdiagnose failure for anyone new to compose.

The correct fix is the application's OWN retry/backoff logic on startup (the same resilience thinking as Circuit Breaker Pattern and Timeout Strategy, applied to a dependency at STARTUP rather than at request time) — or, in more recent compose versions, an explicit condition: service_healthy on depends_on, tied to the dependency's own HEALTHCHECK (Health Checks & Production Best Practices), which DOES wait for genuine readiness rather than just process start.

Environment variable injection

app:
  environment:
    - SPRING_PROFILES_ACTIVE=local   # inline, hardcoded here
  env_file:
    - .env.local                     # OR loaded from a separate file

Environment variables can be set inline per service, or loaded from a .env file — the file-based approach is generally preferred for anything that varies per developer or contains local-only values, since it keeps the compose file itself identical across everyone's machines while letting each developer's .env.local (typically gitignored) hold their own local overrides.

Follow-up questions this topic invites — and their answers

Q: Why not just use condition: service_healthy everywhere instead of relying on app-level retry logic? A: service_healthy is a genuinely good fix WHEN the dependency has a well-defined health check already, but the app's own retry/backoff logic is still valuable as defense-in-depth — a production deployment (Kubernetes, not compose) doesn't have depends_on semantics at all, so an application that only works because compose sequenced things correctly will break in a real orchestrated environment; building retry logic into the app itself is the more portable, universally-correct fix.

Q: Does a local docker-compose stack accurately represent how services actually communicate in production? A: Only loosely — compose's shared bridge network and by-name DNS resolution is conceptually similar to Kubernetes Services (Services & Ingress), but the actual mechanisms differ; compose is valuable for LOCAL functional testing (does my code correctly integrate with a real Postgres/Redis), not as a stand-in for production networking or scaling behavior.

Q: Should local-only values like devpassword ever resemble real credentials? A: No — local dev credentials should be obviously fake/throwaway values, specifically so no one is tempted to reuse them anywhere real, and so an accidentally-committed compose file with a hardcoded local password poses zero actual security risk.

Q: How does a compose-defined volume (like pgdata above) relate to what's covered in Networking & Storage? A: It's the exact same named-volume mechanism — compose's volumes: top-level key just declares it in the same file as the services using it, for convenience; the underlying Docker volume behavior (persists independently of any one container's lifecycle) is identical to running docker volume create manually.

Previous

Networking & Storage

Next

Health Checks & Production Best Practices

AI Tutor

Lesson: docker-compose for Local Development

Quick actions

AI responses can be inaccurate. Verify critical information.