Production Laravel Stack
Production Laravel with Nginx, PHP-FPM, MySQL, Redis, and Laravel Horizon for queues
Overview
Laravel is a PHP web application framework that follows the Model-View-Controller (MVC) architectural pattern, known for its elegant syntax, built-in ORM (Eloquent), and comprehensive tooling ecosystem. This production-ready stack combines Laravel with PHP-FPM for efficient PHP process management, NGINX as a high-performance reverse proxy and static file server, MySQL for reliable relational data storage, Redis for caching and session management, and Laravel Horizon for sophisticated queue monitoring and management. The architecture separates concerns effectively: NGINX handles HTTP requests and serves static assets while proxying dynamic requests to PHP-FPM, MySQL manages persistent data with ACID compliance, Redis provides sub-millisecond response times for caching and session storage, and Horizon offers real-time queue monitoring with automatic worker scaling. This combination is ideal for businesses running Laravel applications that require high availability, scalability, and professional queue management, particularly e-commerce platforms, SaaS applications, and content management systems that need robust background job processing and real-time features.
Key Features
- Laravel Horizon dashboard for real-time queue monitoring with automatic worker balancing
- PHP-FPM process manager with optimized memory usage and request handling
- NGINX event-driven architecture serving static assets and proxying dynamic requests
- Redis-based session storage and caching with sub-millisecond response times
- MySQL InnoDB storage engine with ACID compliance and transaction support
- Laravel scheduler container running artisan commands via cron-like functionality
- Separated application, queue worker, and scheduler containers for scalability
- Production-optimized Laravel configuration with opcache and Redis drivers
Common Use Cases
- 1E-commerce platforms requiring background order processing and inventory management
- 2SaaS applications with user authentication, subscription billing, and email notifications
- 3Content management systems with media processing and scheduled publishing
- 4API-driven applications serving mobile apps and third-party integrations
- 5Multi-tenant applications requiring session isolation and background tenant provisioning
- 6Educational platforms with automated course enrollment and progress tracking
- 7Marketing platforms processing email campaigns and analytics data
Prerequisites
- Docker Engine 20.0+ and Docker Compose 2.0+ installed on the host system
- Minimum 2GB RAM available (1GB for MySQL, 512MB for Redis, 512MB for Laravel containers)
- Port 80 available for NGINX web server (configurable via NGINX_PORT environment variable)
- Generated Laravel APP_KEY using 'php artisan key:generate --show' command
- Basic understanding of Laravel framework, Eloquent ORM, and queue job concepts
- Familiarity with Redis data structures and MySQL database administration
For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms
docker-compose.yml
docker-compose.yml
1services: 2 nginx: 3 image: nginx:alpine4 container_name: laravel-nginx5 restart: unless-stopped6 ports: 7 - "${NGINX_PORT:-80}:80"8 volumes: 9 - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro10 - ./:/var/www/html11 depends_on: 12 - app1314 app: 15 build: 16 context: .17 dockerfile: Dockerfile18 container_name: laravel-app19 restart: unless-stopped20 volumes: 21 - ./:/var/www/html22 environment: 23 - APP_ENV=production24 - APP_KEY=${APP_KEY}25 - APP_DEBUG=false26 - DB_CONNECTION=mysql27 - DB_HOST=db28 - DB_PORT=330629 - DB_DATABASE=${DB_NAME}30 - DB_USERNAME=${DB_USER}31 - DB_PASSWORD=${DB_PASSWORD}32 - REDIS_HOST=redis33 - CACHE_DRIVER=redis34 - SESSION_DRIVER=redis35 - QUEUE_CONNECTION=redis36 depends_on: 37 - db38 - redis3940 horizon: 41 build: 42 context: .43 dockerfile: Dockerfile44 container_name: laravel-horizon45 restart: unless-stopped46 command: php artisan horizon47 volumes: 48 - ./:/var/www/html49 environment: 50 - APP_ENV=production51 - APP_KEY=${APP_KEY}52 - DB_CONNECTION=mysql53 - DB_HOST=db54 - DB_DATABASE=${DB_NAME}55 - DB_USERNAME=${DB_USER}56 - DB_PASSWORD=${DB_PASSWORD}57 - REDIS_HOST=redis58 - QUEUE_CONNECTION=redis59 depends_on: 60 - db61 - redis6263 scheduler: 64 build: 65 context: .66 dockerfile: Dockerfile67 container_name: laravel-scheduler68 restart: unless-stopped69 command: sh -c "while true; do php artisan schedule:run; sleep 60; done"70 volumes: 71 - ./:/var/www/html72 environment: 73 - APP_ENV=production74 - APP_KEY=${APP_KEY}75 - DB_HOST=db76 - DB_DATABASE=${DB_NAME}77 - DB_USERNAME=${DB_USER}78 - DB_PASSWORD=${DB_PASSWORD}79 - REDIS_HOST=redis80 depends_on: 81 - db82 - redis8384 db: 85 image: mysql:8.086 container_name: laravel-db87 restart: unless-stopped88 environment: 89 - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}90 - MYSQL_DATABASE=${DB_NAME}91 - MYSQL_USER=${DB_USER}92 - MYSQL_PASSWORD=${DB_PASSWORD}93 volumes: 94 - mysql_data:/var/lib/mysql9596 redis: 97 image: redis:7-alpine98 container_name: laravel-redis99 restart: unless-stopped100 volumes: 101 - redis_data:/data102103volumes: 104 mysql_data: 105 redis_data: .env Template
.env
1# Laravel Production Stack2NGINX_PORT=8034# Laravel5APP_KEY=base64:your-app-key-here67# Database8DB_ROOT_PASSWORD=root_password9DB_NAME=laravel10DB_USER=laravel11DB_PASSWORD=laravel_passwordUsage Notes
- 1Create Dockerfile with PHP-FPM and Laravel dependencies
- 2Generate APP_KEY: php artisan key:generate --show
- 3Run migrations: docker compose exec app php artisan migrate
- 4Horizon dashboard at /horizon (configure auth)
- 5Scheduler runs artisan schedule:run every minute
- 6Optimize: docker compose exec app php artisan optimize
Individual Services(6 services)
Copy individual services to mix and match with your existing compose files.
nginx
nginx:
image: nginx:alpine
container_name: laravel-nginx
restart: unless-stopped
ports:
- ${NGINX_PORT:-80}:80
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./:/var/www/html
depends_on:
- app
app
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-app
restart: unless-stopped
volumes:
- ./:/var/www/html
environment:
- APP_ENV=production
- APP_KEY=${APP_KEY}
- APP_DEBUG=false
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=${DB_NAME}
- DB_USERNAME=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- CACHE_DRIVER=redis
- SESSION_DRIVER=redis
- QUEUE_CONNECTION=redis
depends_on:
- db
- redis
horizon
horizon:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-horizon
restart: unless-stopped
command: php artisan horizon
volumes:
- ./:/var/www/html
environment:
- APP_ENV=production
- APP_KEY=${APP_KEY}
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_DATABASE=${DB_NAME}
- DB_USERNAME=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
- QUEUE_CONNECTION=redis
depends_on:
- db
- redis
scheduler
scheduler:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-scheduler
restart: unless-stopped
command: sh -c "while true; do php artisan schedule:run; sleep 60; done"
volumes:
- ./:/var/www/html
environment:
- APP_ENV=production
- APP_KEY=${APP_KEY}
- DB_HOST=db
- DB_DATABASE=${DB_NAME}
- DB_USERNAME=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
depends_on:
- db
- redis
db
db:
image: mysql:8.0
container_name: laravel-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
redis
redis:
image: redis:7-alpine
container_name: laravel-redis
restart: unless-stopped
volumes:
- redis_data:/data
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 nginx:5 image: nginx:alpine6 container_name: laravel-nginx7 restart: unless-stopped8 ports:9 - "${NGINX_PORT:-80}:80"10 volumes:11 - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro12 - ./:/var/www/html13 depends_on:14 - app1516 app:17 build:18 context: .19 dockerfile: Dockerfile20 container_name: laravel-app21 restart: unless-stopped22 volumes:23 - ./:/var/www/html24 environment:25 - APP_ENV=production26 - APP_KEY=${APP_KEY}27 - APP_DEBUG=false28 - DB_CONNECTION=mysql29 - DB_HOST=db30 - DB_PORT=330631 - DB_DATABASE=${DB_NAME}32 - DB_USERNAME=${DB_USER}33 - DB_PASSWORD=${DB_PASSWORD}34 - REDIS_HOST=redis35 - CACHE_DRIVER=redis36 - SESSION_DRIVER=redis37 - QUEUE_CONNECTION=redis38 depends_on:39 - db40 - redis4142 horizon:43 build:44 context: .45 dockerfile: Dockerfile46 container_name: laravel-horizon47 restart: unless-stopped48 command: php artisan horizon49 volumes:50 - ./:/var/www/html51 environment:52 - APP_ENV=production53 - APP_KEY=${APP_KEY}54 - DB_CONNECTION=mysql55 - DB_HOST=db56 - DB_DATABASE=${DB_NAME}57 - DB_USERNAME=${DB_USER}58 - DB_PASSWORD=${DB_PASSWORD}59 - REDIS_HOST=redis60 - QUEUE_CONNECTION=redis61 depends_on:62 - db63 - redis6465 scheduler:66 build:67 context: .68 dockerfile: Dockerfile69 container_name: laravel-scheduler70 restart: unless-stopped71 command: sh -c "while true; do php artisan schedule:run; sleep 60; done"72 volumes:73 - ./:/var/www/html74 environment:75 - APP_ENV=production76 - APP_KEY=${APP_KEY}77 - DB_HOST=db78 - DB_DATABASE=${DB_NAME}79 - DB_USERNAME=${DB_USER}80 - DB_PASSWORD=${DB_PASSWORD}81 - REDIS_HOST=redis82 depends_on:83 - db84 - redis8586 db:87 image: mysql:8.088 container_name: laravel-db89 restart: unless-stopped90 environment:91 - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}92 - MYSQL_DATABASE=${DB_NAME}93 - MYSQL_USER=${DB_USER}94 - MYSQL_PASSWORD=${DB_PASSWORD}95 volumes:96 - mysql_data:/var/lib/mysql9798 redis:99 image: redis:7-alpine100 container_name: laravel-redis101 restart: unless-stopped102 volumes:103 - redis_data:/data104105volumes:106 mysql_data:107 redis_data:108EOF109110# 2. Create the .env file111cat > .env << 'EOF'112# Laravel Production Stack113NGINX_PORT=80114115# Laravel116APP_KEY=base64:your-app-key-here117118# Database119DB_ROOT_PASSWORD=root_password120DB_NAME=laravel121DB_USER=laravel122DB_PASSWORD=laravel_password123EOF124125# 3. Start the services126docker compose up -d127128# 4. View logs129docker compose logs -fOne-Liner
Run this command to download and set up the recipe in one step:
terminal
1curl -fsSL https://docker.recipes/api/recipes/production-laravel-stack/run | bashTroubleshooting
- Laravel Horizon shows 'Inactive' status: Ensure Redis connection is working and run 'php artisan horizon:terminate' then restart the horizon container
- MySQL connection refused errors: Verify DB_HOST=db matches the service name and database container is fully initialized before application startup
- PHP-FPM 502 Bad Gateway errors: Check PHP-FPM pool configuration and ensure sufficient worker processes are configured for your traffic load
- Redis connection timeout issues: Increase Redis timeout settings in config/database.php and verify REDIS_HOST environment variable matches service name
- Queue jobs not processing: Confirm QUEUE_CONNECTION=redis is set and Horizon workers are active in the dashboard at /horizon
- Laravel scheduler not running: Verify the scheduler container is running and check Laravel's app/Console/Kernel.php for scheduled task definitions
Community Notes
Loading...
Loading notes...
Download Recipe Kit
Get all files in a ready-to-deploy package
Includes docker-compose.yml, .env template, README, and license
Components
nginxlaravelphp-fpmmysqlredishorizon
Tags
#laravel#php#production#horizon#nginx#mysql#redis
Category
Full Web StacksAd Space
Shortcuts: C CopyF FavoriteD Download