Docker Compose for Production: Complete Setup Guide

docker-compose.yml for Laravel + Nginx + MySQL + Redis — complete production environment. We explain the file line by line.
What is Docker Compose?
Docker Compose is a tool for defining and running multi-container applications. Instead of running each container with a separate docker run command, you write a single YAML file describing all containers, their configurations, and networks — then start everything with one command.
Why Docker Compose for production?
- Define your entire architecture in one version-controlled file
- Start and stop all services with a single command
- Automatic restart on failure
- Organized management of networks and Volumes
Structure of docker-compose.yml
version: '3.8'
services:
# Define services here
volumes:
# Define Volumes here
networks:
# Define networks here
Complete Example: Laravel Application for Production
# File: docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-app
restart: unless-stopped
working_dir: /var/www/app
volumes:
- app-storage:/var/www/app/storage
- .env:/var/www/app/.env
networks:
- app-network
depends_on:
- db
- redis
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/ssl:/etc/nginx/ssl
- app-storage:/var/www/app/storage
networks:
- app-network
depends_on:
- app
db:
image: mysql:8.0
container_name: mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
- ./docker/mysql/my.cnf:/etc/mysql/conf.d/custom.cnf
networks:
- app-network
ports:
- "127.0.0.1:3306:3306"
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
networks:
- app-network
queue:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-queue
restart: unless-stopped
command: php artisan queue:work --sleep=3 --tries=3 --max-time=3600
volumes:
- app-storage:/var/www/app/storage
- .env:/var/www/app/.env
networks:
- app-network
depends_on:
- db
- redis
scheduler:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-scheduler
restart: unless-stopped
command: >
sh -c "while true; do php artisan schedule:run --verbose --no-interaction; sleep 60; done"
volumes:
- app-storage:/var/www/app/storage
- .env:/var/www/app/.env
networks:
- app-network
depends_on:
- db
- redis
volumes:
mysql-data:
driver: local
redis-data:
driver: local
app-storage:
driver: local
networks:
app-network:
driver: bridge
nginx Configuration for Production
# File: docker/nginx/default.conf
server {
listen 80;
server_name example.com;
root /var/www/app/public;
index index.php;
client_max_body_size 100M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location ~ /\.(?!well-known) {
deny all;
}
}
Optimized MySQL Configuration
# File: docker/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
max_connections = 100
slow_query_log = 1
long_query_time = 2
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Running and Management Commands
# Start all services
docker compose up -d
# Stop all services
docker compose down
# Rebuild images
docker compose build --no-cache
# View service status
docker compose ps
# View logs
docker compose logs -f app
docker compose logs -f --tail=100
# Execute command inside a container
docker compose exec app php artisan migrate
docker compose exec app php artisan tinker
docker compose exec db mysql -u root -p
Deployment and Updates
Deployment Script
# File: deploy.sh
#!/bin/bash
set -e
echo "Pulling latest updates..."
git pull origin main
echo "Building images..."
docker compose build app
echo "Running migrations..."
docker compose exec app php artisan migrate --force
echo "Clearing cache..."
docker compose exec app php artisan optimize:clear
docker compose exec app php artisan optimize
echo "Restarting services..."
docker compose up -d --no-deps app queue scheduler
echo "Deployment completed successfully."
Zero-Downtime Update
# Build the new image
docker compose build app
# Start new container while keeping the old one
docker compose up -d --no-deps --scale app=2 app
# Wait for the new container to be ready
sleep 10
# Stop the old container
docker compose up -d --no-deps --scale app=1 app
Monitoring and Health
# Add Health Check to a container
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Resource Monitoring
# Resource usage per container
docker stats
# Disk usage
docker system df
Backups
# Database backup
docker compose exec db mysqldump -u root -p${DB_ROOT_PASSWORD} ${DB_DATABASE} | gzip > backup_$(date +%Y%m%d).sql.gz
# Volume backup
docker run --rm -v mysql-data:/data -v $(pwd):/backup alpine tar czf /backup/mysql-volume-backup.tar.gz /data
Running on CLOUD
CLOUD (cloud.net.sa) servers provide an ideal environment for Docker Compose in production. A VPS with 4 cores and 8GB RAM is sufficient to run a complete Laravel stack with nginx, MySQL, and Redis.
Order your cloud server from CLOUD and start deploying your application with docker compose up -d — ready in under a minute.
Get Started
Ready to build your project? Configure your cloud server with the specs your app needs, or view plans and pricing.
