On this page
Deployment and DevOps
Production Stack Overview
A typical PHP production stack:
Client → Nginx (reverse proxy) → PHP-FPM → Application → Database/Redis
PHP-FPM Configuration
; /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
Tune pm.max_children based on available RAM and average request memory.
Nginx Configuration
server {
listen 80;
server_name example.com;
root /var/www/myapp/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
Laravel/Symfony apps serve from the public/ directory only.
Docker Deployment
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql opcache
COPY . /var/www/html
WORKDIR /var/www/html
RUN composer install --optimize-autoloader --no-dev
# docker-compose.yml
services:
app:
build: .
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
Environment Configuration
# Production .env
APP_ENV=production
APP_DEBUG=false
DB_HOST=db
REDIS_HOST=redis
Never enable debug mode in production — it exposes sensitive information.
CI/CD Pipeline
Example GitHub Actions workflow:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install --no-dev
- run: vendor/bin/phpunit
- run: ./deploy.sh
Zero-Downtime Deployment
- Use blue-green or rolling deployments
- Run migrations before switching traffic
- Warm OPcache after deploy:
curl https://example.com/health
Monitoring
- Logs: centralized logging (ELK, Loki)
- Metrics: request latency, error rates (Prometheus, Datadog)
- Health checks:
/healthendpoint for load balancers - Uptime: external monitoring (UptimeRobot, Pingdom)
Production Checklist
- HTTPS with valid certificate
- OPcache enabled,
validate_timestamps=0 -
display_errors=Off, errors logged to file - Database backups automated
- Secrets in environment variables, not code
- Rate limiting on public APIs
- Dependency updates via Dependabot or Renovate