Home About Skills Products Work
Projects Services Experience
Learn
Tutorials Courses Blogs Resources
Contact
Blog · Docker & DevOps

Docker Compose for Multi-Service Development Environments

Docker Compose for Multi-Service Development Environments

Docker Compose stops being just "one container" and starts earning its keep the moment your local dev environment needs several coordinated services — an app, a database, a cache, maybe a queue worker and a search engine. Here's how to structure a real multi-service setup.

A Realistic Multi-Service Stack

services:
  app:
    build: .
    volumes:
      - .:/var/www
    environment:
      DB_HOST: db
      REDIS_HOST: redis
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  worker:
    build: .
    command: php artisan queue:work --tries=3
    volumes:
      - .:/var/www
    depends_on:
      - db
      - redis

  db:
    image: mysql:8
    environment:
      MYSQL_DATABASE: app
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - dbdata:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      retries: 10

  redis:
    image: redis:alpine

volumes:
  dbdata:

Notice worker reuses the exact same build: . as app — same image, different command. No need to maintain two separate Dockerfiles for what's really the same application running in two different modes.

depends_on with Health Checks

Plain depends_on: [db] only waits for the container to START, not for MySQL to actually be ready to accept connections — a classic source of "connection refused" errors on the very first docker compose up. The condition: service_healthy form waits for the healthcheck to actually pass first.

Named Volumes vs Bind Mounts

volumes:
  - .:/var/www              # bind mount — your local code, live-synced into the container
  - dbdata:/var/lib/mysql   # named volume — Docker-managed, persists across container restarts

Use bind mounts for your application code (so edits show up instantly without rebuilding) and named volumes for anything you want to survive docker compose down — database data being the obvious example.

Environment-Specific Overrides

# docker-compose.override.yml (loaded automatically alongside docker-compose.yml, git-ignored)
services:
  app:
    environment:
      APP_DEBUG: "true"

Docker Compose automatically merges docker-compose.override.yml on top of the base file — a clean way to keep developer-specific tweaks (debug flags, exposed ports) out of the shared, committed configuration.

One Command for the Whole Team

docker compose up -d brings up every service, correctly ordered, with the same versions for every developer on the team — the single biggest reduction in "it works on my machine" bug reports you can make without changing a line of application code.

Dockerizing a Laravel Application: A Complete Guide

Dockerizing a Laravel Application: A Complete Guide

A complete Dockerfile, docker-compose stack, and Nginx configuration for running a Laravel app in containers, from local dev to production.

CI/CD for Web Apps with GitHub Actions

CI/CD for Web Apps with GitHub Actions

Automated testing and deployment with GitHub Actions — running a real database in CI, and deploying automatically on merge to main.

Esc