Production-Ready Docker Setup for Laravel in 2025
Most Docker tutorials for Laravel are toy setups. This is the configuration I actually use in production — multi-stage builds, separate containers, health checks, and zero-downtime deploys.
The Laravel Docker setups you find in most tutorials work on localhost and fall apart the moment you deploy. Over-privileged containers, single-stage builds that ship dev dependencies to production, no health checks, and no thought given to how Nginx and PHP-FPM communicate at scale. Here's the setup I actually run in production.
The Container Architecture
- nginx: serves static assets and proxies PHP requests to php-fpm
- php-fpm: runs the Laravel application via FastCGI
- scheduler: runs 'php artisan schedule:work' in a single dedicated container
- worker: runs 'php artisan queue:work' — scale this horizontally for throughput
- redis: session, cache, and queue backend
Multi-Stage Dockerfile
FROM php:8.3-fpm-alpine AS base
RUN apk add --no-cache libpng-dev libzip-dev \
&& docker-php-ext-install pdo_mysql zip gd opcache
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
FROM base AS deps
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist
FROM base AS production
WORKDIR /app
COPY --from=deps /app/vendor ./vendor
COPY . .
RUN composer dump-autoload --optimize \
&& php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
USER www-dataHealth Checks Matter
Without health checks, Docker Compose (and Kubernetes) will route traffic to containers that are still booting or have crashed. Add a health check to your php-fpm service that pings php-fpm's status endpoint, and configure nginx to only pass traffic once php-fpm reports healthy.
Environment Variable Strategy
Never commit .env files to your image. Pass secrets at runtime via Docker secrets or environment variables from your orchestration layer. In production I use a startup script that reads from AWS Secrets Manager and writes a .env file before php-fpm starts. The image itself contains zero secrets.
Zero-Downtime Deploys
Blue-green deployment with Docker is straightforward: build the new image, push to registry, update your service to use the new tag, and Docker Swarm or ECS will drain connections from old containers before terminating them. The key is ensuring your migrations are backward-compatible — the old code must run against the new schema during the transition window.
OPcache Configuration
OPcache is the single biggest PHP performance lever and is almost always misconfigured in Docker setups. Set opcache.validate_timestamps=0 in production (the filesystem doesn't change at runtime), opcache.memory_consumption=256, and opcache.max_accelerated_files=10000. Wrong values here can cut your Laravel response times in half.