Deployment Guide
Deployment Guide
1 · Production Checklist
Item | Target |
---|---|
PHP version | 8.4 with OPcache enabled, memory_limit >= 256 M |
Extensions | pdo_mysql (or pgsql), openssl, json, mbstring |
Web server | Nginx 1.20 + PHP-FPM or Apache 2.4 mod_php/FPM |
Database | MySQL 8.0 / MariaDB 10.6 |
Filesystem | Writable: storage/, public/uploads/, cache dirs |
Env vars | APP_ENV=prod, APP_DEBUG=false, JWT_SECRET=… |
Secrets | JWT RSA keys (storage/keys/), DB password, Redis URL |
2 · Build the release
# 1. Pull code
git clone git@github.com:acme/myapp.git /var/www/myapp
cd /var/www/myapp
# 2. Install deps (no dev)
composer install --no-dev --optimize-autoloader --apcu-autoloader
# 3. Cache config & compile views
php vendor/bin/ml config:cache
php vendor/bin/ml view:clear # no-op if nothing cached yet
# 4. Generate RSA keys if fresh server
php vendor/bin/ml key:generate --force
Tip: commit the public key; keep the private key out of VCS.
3 · Database migrations & seeders
php vendor/bin/ml migrate --env=prod
php vendor/bin/ml db:seed --class=InitialUserSeeder
Each batch is wrapped in a transaction; failures roll back automatically.
4 · Nginx + PHP-FPM config
/etc/nginx/sites-available/myapp.conf
server {
listen 443 ssl http2;
server_name app.example.com;
root /var/www/myapp/public;
index index.php;
ssl_certificate /etc/letsencrypt/live/app/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app/privkey.pem;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# Static assets – 365 d
location ~* \.(css|js|png|jpe?g|svg|woff2?)$ {
expires 365d;
access_log off;
try_files $uri =404;
}
# Healthcheck
location = /healthz { access_log off; return 200 'OK'; }
# Front controller
location / {
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Enable and reload:
ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
5 · Environment variables
Create /etc/myapp.env (or use a secrets manager):
APP_ENV=prod
APP_DEBUG=false
DB_HOST=127.0.0.1
DB_DATABASE=myapp
DB_USER=myapp
DB_PASS=SuperSecret
JWT_SECRET="file:///var/www/myapp/storage/keys/private.pem"
REDIS_URL=redis://127.0.0.1:6379
Add to FPM pool config:
; /etc/php/8.4/fpm/pool.d/myapp.conf
[myapp]
user = www-data
group = www-data
listen = /run/php/php8.4-fpm.sock
env[APP_ENV] = prod
env[APP_DEBUG] = false
env[DB_HOST] = 127.0.0.1
...
Reload FPM: systemctl reload php8.4-fpm.
6 · Queue / cron workers (optional)
# Run queue every minute via systemd or supervisor
* * * * * www-data php /var/www/myapp/artisan queue:work --stop-when-empty
# Schedule commands
* * * * * www-data php /var/www/myapp/artisan schedule:run >> /dev/null 2>&1
7 · Zero-downtime deploys (blue-green)
Deploy to /var/www/myapp-<hash>
Run composer, migrations, cache-clear
ln -sfn myapp-<hash> /var/www/myapp_current
Point Nginx root to /var/www/myapp_current/public and reload
8 · Container / Kubernetes
Dockerfile (slim):
FROM php:8.4-fpm-alpine
RUN apk add --no-cache nginx supervisor \
&& docker-php-ext-install pdo_mysql
WORKDIR /var/www/html
COPY . .
RUN composer install --no-dev --optimize-autoloader
EXPOSE 80
CMD ["php-fpm"]
Helm chart sample values:
image:
repository: ghcr.io/acme/myapp
tag: v1.2.0
env:
- name: APP_ENV
value: prod
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-creds
key: host
probes:
liveness: /healthz
readiness: /healthz
9 · Monitoring & logging
Concern | Solution |
---|---|
Metrics | Expose /metrics (Prometheus) via Telemetry package |
Logs | Monolog → syslog or JSON → Loki/Elastic |
Tracing | Wrap Connection + HTTP client with an OpenTelemetry decorator |
Alerts | Subscribe to MigrationEvent, RequestEvent, push to Slack |
10 · Hardening tips
Disable app.debug in production (config/app.mlc).
Serve uploads from a separate sub-domain if possible (files.example.com).
Run php -d detect_unicode=0 vendor/bin/ml security:audit monthly (upcoming CLI command).
Keep Composer up-to-date (composer self-update && composer outdated --direct).
Next steps
Autoscaling on GCP – see the Google Cloud guide.
CI/CD pipeline – follow our GitHub Actions template.
Blue/green database migrations – planned for Migration 2.0.
Happy shipping! 🚀