NightFox

NightFox

Deploying and Operating PHP Applications

Two beliefs drive this page. Deployments should be repeatable, visible, and easy to undo. FTPing files over one at a time while the site is live is none of those. And performance work should start with measuring, not guessing. The slow part is almost never where you think it is.

Atomic release layout

/var/www/customer-portal/
├── current -> releases/20260716-134500
├── releases/
│   ├── 20260715-101200/
│   └── 20260716-134500/
└── shared/
    ├── .env
    ├── storage/
    └── uploads/

Build the whole release in its own folder, run your checks, link in the shared writable stuff (uploads, logs), then flip the current symlink. Rolling back is just flipping it back, as long as the database is still compatible with the old code.

A deployment pipeline

  1. Install dependencies from the lock file.
  2. Run syntax checks, static analysis, and tests.
  3. Build front-end assets if applicable.
  4. Create the release directory and copy the build.
  5. Run backward-compatible database migrations.
  6. Warm caches and optimize autoloading.
  7. Switch traffic to the new release.
  8. Reload PHP-FPM and restart workers gracefully.
  9. Run health checks and verify logs and metrics.

Safe database migrations

For database changes that can't happen in an instant, I use expand and contract:

  1. Add the new column or table without removing the old one.
  2. Deploy code that can work with both shapes.
  3. Backfill data in controlled batches.
  4. Switch reads and writes to the new shape.
  5. Remove the old structure in a later release.

PHP-FPM capacity

Size your PHP-FPM pool from how much memory each worker actually uses and how many requests you really get at once. Too few workers and requests pile up waiting. Too many and you run out of memory and the whole server gets unstable. Watch active and idle workers, how often you hit max_children, request times, and upstream timeouts.

OPcache

Turn OPcache on in production so PHP isn't re-parsing every file on every request. It's free performance. If you deploy with the symlink approach, either be careful with timestamp validation or reload PHP-FPM during the deploy so OPcache doesn't keep serving the old code.

A performance workflow

  1. Define the slow user action or endpoint.
  2. Measure total request time and break it into application, SQL, external API, and rendering time.
  3. Find the largest component.
  4. Change one thing.
  5. Measure again under realistic load.

Common high-value improvements

  • Add a missing database index discovered through EXPLAIN.
  • Remove N+1 queries.
  • Paginate large result sets.
  • Cache stable expensive results with clear invalidation.
  • Move email, exports, and API synchronization to background jobs.
  • Set connection and request timeouts.
  • Compress and cache static assets at the web server or CDN.
Health checks: "the process is running" isn't enough. Separate "am I alive?" from "am I ready for traffic?", and check the critical dependencies, without making the health check itself so heavy that it causes load.

Last Modified: October 12, 2026, 11:46 am