php runtime and server environment
Docker Compose For Local Services
The important skill is understanding service boundaries. Inside Compose, containers talk to each other by service name, not by localhost. From your host machine, you usually use published ports.
A typical local shape
services:
web:
image: nginx:1.27
ports:
- "8080:80"
volumes:
- .:/var/www/app
depends_on:
- php
php:
build: .
volumes:
- .:/var/www/app
environment:
DB_HOST: database
CACHE_HOST: redis
database:
image: mysql:8.4
environment:
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: local-password
MYSQL_ROOT_PASSWORD: root-password
volumes:
- database-data:/var/lib/mysql
redis:
image: redis:7
volumes:
database-data:
The host opens http://localhost:8080. The PHP container connects to MySQL using host database, because that is the Compose service name.
Service names are DNS names
Inside the php container:
DB_HOST=database
CACHE_HOST=redis
Using localhost from inside php means "the PHP container itself", not the MySQL container and not your laptop.
This is one of the most common Compose mistakes for PHP developers.
Database and cache persistence
Named volumes keep data after containers stop:
volumes:
database-data:
That is convenient, but it means local data can become stale. When debugging strange database behaviour, know whether the project expects you to reset the volume, run migrations, or seed data.
Startup Order Is Not Readiness
depends_on controls startup order. It does not prove that MySQL has finished starting or that Redis is ready to accept connections.
A PHP container may start before the database is usable. The application should handle short-lived connection failures sensibly, and local tooling may need a health check or a small wait script before migrations run.
database:
image: mysql:8.4
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
When a local stack fails only during startup, check readiness before changing application code.
Published Ports And Internal Ports
A published port lets the host reach a container:
ports:
- "8080:80"
The host uses port 8080. The web container still listens on port 80.
Containers on the same Compose network usually do not need published ports to reach one another. Publish a database port only when a host tool, such as a desktop SQL client, needs it. Avoid exposing services without a reason.
Queue workers
A local queue worker is often another service using the same PHP image:
worker:
build: .
command: php artisan queue:work
volumes:
- .:/var/www/app
depends_on:
- database
- redis
The exact command depends on the framework. The concept is the same: the worker is a separate long-running PHP process and needs the same code, environment, and service access as the web application.
Local secrets
Compose files often contain local-only passwords. Do not copy production secrets into a local Compose file. Use .env files carefully and keep real secrets out of Git.
Commands You Will Use Often
Learn the basic lifecycle commands:
docker compose up -d
docker compose ps
docker compose logs php
docker compose exec php php -v
docker compose exec php composer install
docker compose down
Use docker compose ps to see which services are running. Use logs when a service exits or a request fails. Use exec to run PHP and Composer in the actual project container.
If the Dockerfile or installed extensions change, rebuild the image:
docker compose up -d --build
What you should be able to do
After this lesson, you should be able to read a Compose file, identify PHP, web, database, cache, and worker services, explain service-name networking, understand volumes and published ports, distinguish startup order from readiness, and diagnose the common mistake of using localhost inside a container.
Practice
Task: Sketch A Compose Setup
Write a small Compose-style outline for a local PHP application.
Requirements
- Include separate
web,php,database, andredisservices. - Show PHP connecting to the database by service name.
- Include a named database volume.
- Include one queue worker service or explain where it would fit.
- Add a short note explaining why
localhostis usually wrong from inside the PHP container.
Check your work
The answer should make service boundaries clear enough for a new developer to debug connection problems.
Show solution
services:
web:
image: nginx:1.27
ports:
- "8080:80"
volumes:
- .:/var/www/app
depends_on:
- php
php:
build: .
volumes:
- .:/var/www/app
environment:
DB_HOST: database
REDIS_HOST: redis
database:
image: mysql:8.4
environment:
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: local-password
MYSQL_ROOT_PASSWORD: root-password
volumes:
- database-data:/var/lib/mysql
redis:
image: redis:7
worker:
build: .
command: php artisan queue:work
volumes:
- .:/var/www/app
depends_on:
- database
- redis
volumes:
database-data:
From inside the php container, database and redis are DNS names for the other Compose services. localhost would point back to the PHP container itself, so it is usually wrong for database and cache connections inside Compose.