Skip to main content
Tutorial

How to set up auto-deployment for Laravel with zero-downtime

The Reflex Team7 minJanuary 2026

"Zero-downtime" does not mean your deploy finishes before users notice. It means users never hit a half-written tree of files while composer install is mid-flight.

The symlink model

Classic Capistrano-style layout:

/releases/20260115120000
/releases/20260115130502
/current -> releases/20260115130502

The web root points at current/public. A deploy builds a new release directory, then atomically repoints current. Nginx never sees a hybrid of Tuesday's app/ and Wednesday's vendor/.

Shared paths

storage/ and sometimes bootstrap/cache/ stay shared across releases so uploads and compiled caches survive the swap—permissions and ownership must be consistent (deploy + www-data group is a common pattern).

Queue and Octane gotchas

If queue workers keep old code in memory, your web tier can be new while jobs still run old serializers—fun bugs await. After a cutover you still need a coordinated php artisan queue:restart (or Horizon equivalent) when your policy says it is safe.

Config cached to a release path can also bite: always know which .env and which bootstrap/cache/config.php your processes read post-swap.

How Reflex Pipeline fits

Pipeline treats releases as immutable artifacts with a recorded deploy marker. Health checks gate traffic shifts where your tier supports it, and rollback is a product feature—not a wiki page nobody has tested.

Minimal manual checklist (any host)

  1. Build in a fresh release directory.
  2. Run migrations with a plan for backward compatibility.
  3. Flip current only when the tree is complete.
  4. Restart workers that cache PHP bytecode.
  5. Watch errors for fifteen minutes like a responsible adult.

We built Reflex because we got tired of teams "doing zero-downtime" on PowerPoint while production was still a symlink race on a Friday. Pipeline is our answer—opinionated, auditable, and Laravel-native.