Your Laravel app works perfectly on localhost. You push it to production — and it breaks. Different PHP versions, MySQL configurations, missing extensions. The problem is environment inconsistency. We at Meteora Web have seen clients waste days debugging environment mismatches. Docker solves this by creating one identical environment for every stage. For Laravel, the setup is clean, fast, and production-ready.
Why use Docker with Laravel for development and production?
Let’s start with a real scenario: your local machine runs PHP 8.3, the server runs PHP 8.1. A function deprecated in 8.1 works locally but crashes in production. Or your local database is MariaDB, the server is MySQL 5.7 — queries behave differently. Docker eliminates these issues by using the same image everywhere. We use Docker for every Laravel project we build: the environment is versioned alongside the code, not left to memory.
The real benefits we see daily
- No more "it works on my machine" — environment is 100% replicable.
- Onboard new developers in minutes — clone the repo, run
docker compose up -d, done. No manual PHP/MySQL/Node setup. - Production matches development — same
docker-compose.ymlwith minor environment overrides. - Saves hours of debugging — environment differences cause most production bugs.
Coming from a background in accounting and ERP systems, we frame this as a cost issue: every hour spent debugging an environment problem is billable time wasted. Docker removes that waste.
Sponsored Protocol
How to set up docker-compose.yml for Laravel?
The docker-compose.yml file defines your services: app (PHP-FPM), web (Nginx), database (MySQL/Postgres), plus optional ones (Redis, Mailpit). Here’s a working example we use for our Laravel projects:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel_app
restart: unless-stopped
working_dir: /var/www
volumes:
- .:/var/www
- ./storage:/var/www/storage
networks:
- laravel
web:
image: nginx:alpine
container_name: laravel_web
restart: unless-stopped
ports:
- "8080:80"
volumes:
- .:/var/www
- ./nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- laravel
depends_on:
- app
db:
image: mysql:8.0
container_name: laravel_db
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: root
ports:
- "3307:3306"
volumes:
- dbdata:/var/lib/mysql
networks:
- laravel
volumes:
dbdata:
networks:
laravel:
Practical steps to get it running
1. Create the Dockerfile for the PHP image. This defines how the app service is built:
Sponsored Protocol
FROM php:8.3-fpm-alpine
RUN docker-php-ext-install pdo_mysql bcmath
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
COPY . .
RUN composer install --no-interaction --prefer-dist --optimize-autoloader
2. Configure Nginx. Create a file called nginx.conf in your project root:
server {
listen 80;
index index.php;
server_name localhost;
root /var/www/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
3. Start the environment. From your Laravel project root, run:
Sponsored Protocol
docker compose up -d
Your app will be available at http://localhost:8080.
How to optimize the Docker image for production?
For production, the image must be small, secure, and ready to run. The golden rule: do not include Composer, npm, or build tools in the final image. Use a multi-stage build to separate the build phase from the runtime phase.
Multi-stage Dockerfile example
# Stage 1: Build
FROM php:8.3-cli-alpine AS build
WORKDIR /app
COPY . .
RUN docker-php-ext-install pdo_mysql bcmath
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN composer install --no-dev --optimize-autoloader
# Stage 2: Runtime
FROM php:8.3-fpm-alpine
COPY --from=build /app /var/www
RUN docker-php-ext-install pdo_mysql bcmath opcache
COPY php.ini-production /usr/local/etc/php/php.ini
WORKDIR /var/www
With multi-stage, the final image is ~200 MB instead of over 1 GB. We use this for every deployment — it reduces storage costs and speeds up pull times on servers.
Sponsored Protocol
How to manage environment variables in development vs production?
Laravel uses the .env file. In development, mount it as a volume. In production, embed it in the image or inject via container environment variables. The cleanest approach: have a separate .env.production file and copy it to .env during CI/CD before building the image.
Practical scheme
- Development:
.envfile mounted as volume. Database points todb:3306. - Production:
.envgenerated by CI/CD pipeline with real values (remote database, API keys). Never committed to the repo. - Key differences:
APP_ENV=production,APP_DEBUG=false, differentDB_HOST.
# On the production server, run:
docker run -d --env-file .env.production my-laravel-app:latest
Should you use Laravel Sail for development?
Laravel Sail is an official Docker wrapper. We recommend it to clients who don’t want to write Docker files manually. Sail handles MySQL, Redis, Mailhog with a single command: sail up -d. It’s great for development. For production, we prefer a custom setup with multi-stage builds and server-specific optimizations.
Sponsored Protocol
When we skip Sail
Sail gives you less control over the production image. We built a proprietary platform for managing multiple client sites: to optimize hosting costs and performance, we use custom multi-stage images with Nginx built in. Sail doesn’t support multi-stage natively and requires a separate Nginx container. For small projects, Sail is fine. For scalability, custom wins.
What to do now
- Get a Laravel test project — if you don’t have one, run
composer create-project laravel/laravel test-docker. - Copy the files (
docker-compose.yml,Dockerfile,nginx.conf) from this guide — adapt them to your project. - Start the environment with
docker compose up -dand test atlocalhost:8080. - Test the differences — change a production variable (e.g.
APP_DEBUG=false) and verify the behavior changes. - Document the setup — if you work in a team, add a
README.mdwith the basic commands. It will reduce onboarding time to minutes.
For the full picture, read our main guide on Docker and Containerization for Italian SMEs. For official Laravel docs on Sail, check the Laravel documentation.