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.
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.
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.
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.
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.
# 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.
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.
A complete Dockerfile, docker-compose stack, and Nginx configuration for running a Laravel app in containers, from local dev to production.
Automated testing and deployment with GitHub Actions — running a real database in CI, and deploying automatically on merge to main.