f in x
Docker Compose for local development: painless multi-container orchestration
> cd .. / HUB_EDITORIALE
Analisi dei dati e metriche

Docker Compose for local development: painless multi-container orchestration

[2026-06-11] Author: Ing. Calogero Bono

Have you ever opened four terminals to start your database, backend, frontend, and Redis every time you begin developing? And then had to remember long commands with ports, volumes, and networks? That's exactly the problem Docker Compose solves in one go.

We, at Meteora Web, use it daily. We coordinate local development for clients with complex stacks (PHP + MySQL + Redis + Node, or Laravel + PostgreSQL + Mailpit), and Docker Compose lets us replicate the production environment on every machine in seconds. No more "it works on my machine".

What is Docker Compose and why you need it

Docker Compose is a tool for defining and running multi-container applications. You write a docker-compose.yml file in YAML, describe services, networks, and volumes, and with a single command everything starts together.

Why it beats launching containers manually:

  • Reproducibility: everything is declared in a file you can version with Git.
  • Shareability: the whole team uses the same environment, zero differences.
  • Isolation: each project has its own dependencies, no version conflicts (PHP, Python, Node, databases).
  • Speed: docker compose up and you're up and running.

Your first docker-compose.yml

Let's take a real example: a PHP web application (Laravel) with MySQL and Redis for caching. Here's the file:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      DB_HOST: db
      REDIS_HOST: redis

  db:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: myapp
      MYSQL_USER: user
      MYSQL_PASSWORD: userpass
    volumes:
      - dbdata:/var/lib/mysql

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

volumes:
  dbdata:

Key parts explained:

Services

Each container you need is a service. app is built from a Dockerfile, db and redis use public images. Service names become network hostnames: your PHP connects to MySQL using db as host.

Volumes

dbdata is a named volume: database data survives container stop and restart. The bind mount .:/var/www/html syncs your source code: you change a file and the server sees it immediately (no rebuild needed).

Dependencies

depends_on tells Docker Compose to start db and redis first. It doesn't guarantee the database is ready to accept connections (that requires health checks), but the startup order is correct.

Essential commands for daily work

Here are the commands you'll use every day:

CommandWhat it does
docker compose up -dStart all services in the background (detached).
docker compose downStop and remove containers, networks, but not volumes.
docker compose buildRebuild images (if you modified the Dockerfile).
docker compose logs -fFollow logs of all services in real time.
docker compose exec app php artisan migrateRun a command inside the app container (e.g., a migration).

We run docker compose up -d every morning and docker compose down at night. Simple, fast, reproducible.

Environment variables and .env file

Hardcoding passwords in docker-compose.yml is a no-go. Use a .env file in the same directory. Docker Compose reads it automatically if you use ${VAR} syntax.

db:
  image: mysql:8.0
  environment:
    MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
    MYSQL_DATABASE: ${DB_NAME}

In your .env write:

DB_ROOT_PASS=supersecret
DB_NAME=myapp

Important: don't commit the .env file to Git (add it to .gitignore). Share a .env.example with the team.

Overrides for local development

Often you want a clean docker-compose.yml for production, but locally you need to mount source code, enable xdebug, expose more ports. Docker Compose supports override files:

  • docker-compose.yml — base (shared with production).
  • docker-compose.override.yml — local additions (can be versioned or not).

Example override for development:

services:
  app:
    volumes:
      - .:/var/www/html
    environment:
      XDEBUG_MODE: debug
      XDEBUG_CONFIG: client_host=host.docker.internal

Run docker compose up -d and the override is applied automatically.

Health checks and waiting for the database

depends_on does not wait for the database to be ready. For apps that crash if the DB isn't available, add health checks:

db:
  image: mysql:8.0
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 5s
    timeout: 5s
    retries: 5

app:
  depends_on:
    db:
      condition: service_healthy

Now the app container starts only after MySQL is healthy. Fewer broken tests.

Common mistakes and how to avoid them

Port conflicts: if you have a local MySQL on port 3306, either map the container to a different port (e.g., 3307) or stop your local service. Better yet: use internal ports and connect containers via the internal network.

Volume permissions: on Linux, files created by the container are owned by root. To use your local user, set the UID in the Dockerfile or use the user parameter in the service. We solve this by creating a dedicated user in the Dockerfile.

Cache and rebuilds: if you modify the Dockerfile, remember to docker compose build before up. Use layer caching efficiently (order instructions from least to most frequently changing).

Concrete workflow with Laravel

Here's how we use Docker Compose in a real project:

  1. Clone the repository.
  2. Copy .env.example to .env and fill in your variables.
  3. Run docker compose up -d.
  4. Enter the container: docker compose exec app bash.
  5. Install dependencies: composer install.
  6. Generate a key: php artisan key:generate.
  7. Run migrations: php artisan migrate.
  8. Open http://localhost:8080.

All done in under 5 minutes on any machine with Docker installed.

What to do now

If you don't have a docker-compose.yml for your project, here's a checklist:

  • Identify the services you need (web, db, cache, queue, etc.).
  • For each service, choose an official image or write a Dockerfile.
  • Define which ports to expose (only those needed in development).
  • Mount your source code with a bind mount for hot reload.
  • Use a named volume for persistent data (database).
  • Add environment variables with a .env file.
  • Test with docker compose up -d and verify connectivity between services.

We at Meteora Web have automated this setup for every new project. If you want to dive deeper, check the official Docker Compose documentation. It's clear and well-written.

Remember: a reproducible development environment is not a luxury — it's an investment. It removes the burden of configuration headaches and lets you focus on what matters: writing great code.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()