Lập trình · 18/09/2026

Docker Compose: Building a Consistent Development Environment

“It works on my machine” usually means the development environment is not described clearly enough. When every team member installs PHP, a database, a cache, and supporting tools differently, version and configuration problems eventually follow. Docker Compose turns those dependencies into configuration that can live with the source code and be recreated consistently.

Docker Compose: Xây dựng môi trường lập trình đồng nhất cho cả nhóm

“It works on my machine” usually means the development environment is not described clearly enough. When every team member installs PHP, a database, a cache, and supporting tools differently, version and configuration problems eventually follow. Docker Compose turns those dependencies into configuration that can live with the source code and be recreated consistently.

What problem does Docker Compose solve?

A web application is rarely just source code. It may require an application server, MySQL or PostgreSQL, Redis, a fake mail service, and a queue worker. Compose describes these components as services together with the networks, volumes, and environment variables they need.

Instead of maintaining a long setup guide and hoping everyone follows it identically, the team can create the same service topology with one command. This is especially useful when onboarding a developer, replacing a computer, or reproducing a bug.

A small configuration that remains readable

services:
  app:
    build: .
    ports:
      - "8080:80"
    volumes:
      - ./:/var/www/html
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: app
      MYSQL_USER: app
      MYSQL_PASSWORD: local_password
      MYSQL_ROOT_PASSWORD: local_root_password
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine

volumes:
  db_data:

depends_on expresses startup dependencies, while healthcheck tells Compose when the database is ready to accept connections. That is more reliable than merely waiting for a container process to start.

Understand the main components

ComponentPurposePractical note
ImageA read-only template used to create containersPin an intentional version instead of depending vaguely on latest
ContainerA running process created from an imageIt should be replaceable; do not use its internal filesystem for durable data
VolumePersistent storage for data such as a databaseDelete it only when local data is no longer needed
NetworkAllows services to communicate by nameThe app connects to db or redis, not localhost

Environment variables and secrets

Compose can declare variables directly, interpolate them from the shell, or read environment files. A file containing real passwords should not be committed. Provide a safe .env.example and deliver staging or production secrets through the deployment platform's secret-management mechanism.

A containerized local environment is not a production architecture by itself. Production still needs deliberate secrets handling, backups, monitoring, upgrades, and recovery procedures.

The daily workflow

docker compose up -d --build
docker compose ps
docker compose logs -f app
docker compose exec app php artisan migrate
docker compose down

Add -v to docker compose down only when you intentionally want to remove volumes. For a local database containing useful test data, that flag can erase substantial setup work.

Improve the developer experience

  • Create a development-specific Dockerfile when the team needs a debugger or code-quality tools.
  • Use a bind mount for source code, but avoid dependency mounts that cause slow host filesystem I/O.
  • Add healthchecks to databases and message brokers the application must wait for.
  • Pin image versions intentionally and schedule upgrades.
  • Put frequent commands in a script or Makefile so new contributors do not memorize long command lines.

Common problems

The app cannot connect to the database

Inside the Compose network, DB_HOST should be the service name, such as db, rather than 127.0.0.1. Also inspect the internal port, health status, and logs from both services.

Source changes do not appear

Check the volume path, working directory, and framework caches. A dependency or operating-system extension change requires an image rebuild; ordinary source changes only require a correct bind mount.

Data disappears after a restart

The database needs a named volume mounted at the image's correct data directory. Removing that volume or changing the Compose project name can also make the system start with a new data store.

Team adoption checklist

  1. A new developer can start the project from the README without verbal instructions.
  2. Image versions are explicit and have an upgrade plan.
  3. The repository contains no real passwords, tokens, or customer data.
  4. Databases and required dependencies have useful healthchecks.
  5. Migration, seed, test, and log commands are documented.
  6. The team has deleted containers and rebuilt them to confirm that required data persists.

Conclusion

Docker Compose does not eliminate every difference between local and production, but it gives the development team a clear environment contract. Start with the services the application truly needs, standardize daily commands, and test a clean rebuild.

References

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

No comments yet. Be the first to share your thoughts.