| name | php-fpm-nginx-deployment |
| description | PHP-FPM + Nginx deployment patterns for the Laravel container in this monorepo. Use when modifying the Dockerfile, nginx.conf, supervisord.conf, entrypoint.sh, or Docker Compose files. Triggers on tasks involving containerization, process management, static file serving, or production deployment configuration. |
| frameworks | ["laravel","nginx","docker"] |
| languages | ["php"] |
| category | deployment |
| updated | "2026-04-29T00:00:00.000Z" |
PHP-FPM + Nginx Deployment Skill
Quick Reference
When to Use: Modifying Docker setup, Nginx config, supervisord, entrypoint, or Compose files
Single container — one Docker image runs both Nginx and PHP-FPM via supervisord
Location: apps/laravel/ (Dockerfile, nginx.conf, supervisord.conf, entrypoint.sh)
Container Architecture
apps/laravel/
├── Dockerfile # Multi-stage build: deps → app → final
├── nginx.conf # Nginx virtualhost (serves static, proxies PHP to FPM)
├── supervisord.conf # Process supervisor (nginx + php-fpm)
└── entrypoint.sh # Bootstrap (migrate, optimize, start supervisord)
Dockerfile Structure
# Stage 1: Composer dependencies
FROM composer:2 AS deps
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Stage 2: Final image
FROM php:8.4-fpm-alpine AS final
RUN apk add --no-cache nginx supervisor
# PHP extensions
RUN docker-php-ext-install pdo pdo_mysql
COPY --from=deps /app/vendor ./vendor
COPY . /var/www/html/
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
COPY nginx.conf /etc/nginx/http.d/default.conf
COPY supervisord.conf /etc/supervisord.conf
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]
Nginx Config
# nginx.conf
server {
listen 80;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
supervisord.conf
[supervisord]
nodaemon=true
logfile=/dev/stdout
logfile_maxbytes=0
[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
entrypoint.sh
#!/bin/sh
set -e
cd /var/www/html
if [ -z "$APP_KEY" ]; then
php artisan key:generate --force
fi
php artisan migrate --force
php artisan optimize
exec /usr/bin/supervisord -c /etc/supervisord.conf
Docker Compose (Development)
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-secret}
MYSQL_DATABASE: ${DB_DATABASE:-laravel}
MYSQL_USER: ${DB_USERNAME:-laravel}
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
retries: 5
api:
build:
context: apps/laravel
dockerfile: Dockerfile
ports:
- "8000:80"
env_file:
- path: apps/laravel/.env
required: false
depends_on:
mysql:
condition: service_healthy
volumes:
- ./apps/laravel:/var/www/html
- laravel_vendor:/var/www/html/vendor
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:80/api/v1/health"]
interval: 10s
retries: 5
volumes:
mysql_data:
laravel_vendor:
The laravel_vendor named volume pins the vendor directory inside the container so host changes don't overwrite Composer-installed packages.
Staging Deployment (EC2 + Docker Compose)
services:
api:
image: ${ECR_REGISTRY}/${ECR_REPOSITORY_API}:${IMAGE_TAG}
env_file: .env.staging
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8
volumes:
- mysql_data:/var/lib/mysql
restart: unless-stopped
Production Deployment (EC2 ALB + ASG)
- Images pushed to ECR; EC2 instances pull them via cloud-init on launch
entrypoint.sh runs migrations and php artisan optimize on container start
- Secrets injected as environment variables via GitHub Secrets → deploy workflow →
.env on EC2
- Health check:
GET /api/v1/health → 200
Common Troubleshooting
docker compose ps
docker compose logs -f api
docker compose exec api sh
docker compose exec api supervisorctl status
docker compose exec api php artisan migrate
docker compose exec api php artisan optimize:clear