lorisleiva/laravel-deployer
Laravel Deployer offers preconfigured Deployer recipes and Artisan commands for zero-downtime Laravel deployments. It streamlines atomic releases, rollbacks, shared files, migrations, and server setup so you can ship changes safely and repeatably.
Package posture: This is a pre-1.0 (0.3.x) Laravel/PHP package whose surface area is narrow and opinionated: it wraps migration execution semantics (specifically once() behavior) so migrations are coordinated rather than blindly re-run. That narrowness is a strength for evaluation — the blast radius is confined to the migration/deploy path rather than request handling — but it also means the package sits directly on the critical path of every deployment.
Architecture fit: Fits the standard Laravel model cleanly: a service provider, an Artisan-facing entry point, and reliance on the framework's existing migration repository and event lifecycle. It does not appear to require a custom migration runner or schema abstraction, which keeps it composable with existing php artisan migrate workflows and CI pipelines. Fit is best in multi-node/autoscaling deployments where concurrent migration attempts are a real hazard; in single-host or single-container environments the value proposition is largely redundant.
Integration feasibility: Low-to-moderate effort. Adoption is configuration plus a command swap in deploy scripts, not an application-code refactor. The main integration work is not technical but procedural — updating deploy runbooks, container entrypoints, and any CI job that invokes migrate directly.
Technical risk — the material item in v0.3.5: Release #208 changes the default so once() migrates on multiple hosts rather than a single host. This is a default-behavior change, not an additive feature. Consequences:
0.3.5). Pre-1.0 semver permits this, but the change is behaviorally closer to a minor/major. Treat version numbers here as advisory and diff behavior, not version strings.Key questions to resolve before adoption:
once() (DB advisory lock, cache lock, atomic insert), and what happens when the lock backend is unavailable — fail-closed or fail-open?once() safe against non-transactional DDL (MySQL implicit commits), or is it only guaranteeing "one runner," not "resumable"?Stack fit: Compatible with a conventional Laravel stack (Artisan console kernel, service provider auto-discovery, standard migrations table). No framework-forking or vendor patches required. Works alongside existing tooling — it does not replace the migrator, it constrains when it runs.
Migration path:
^0.3. Pin 0.3.5 in composer.json and lock it, precisely because default-behavior changes are arriving in patch releases.migrations table contents. Rollback is a config/version revert, though note that a rollback after a multi-host run leaves no cleanup work beyond the already-applied migrations.Compatibility: Validate against our specific Laravel major and PHP version, plus our cache/session/queue drivers, since the coordination primitive is the compatibility-sensitive piece. Verify behavior with whichever lock store production actually uses — not with the array/file driver used in local dev, which will make concurrency bugs invisible.
Sequencing:
Maintenance: Low day-to-day surface, but non-zero upgrade cost. Pre-1.0 with behavior changes landing in patch releases means every bump deserves a changelog read and a diff review. Budget for this rather than treating it as a silent dependency update; automated dependency bots will happily open the PR that flips our migration semantics.
Support: External support is community/maintainer-driven, likely small. Internal ownership must be assigned — realistically the platform/infra team that owns deploy pipelines, not individual product teams. Document the chosen configuration and the reasoning, because the default is now non-obvious to a future reader.
Scaling: This is where the release matters most and where it helps. Multi-host-by-default is a genuine improvement for horizontally scaled deployments: it removes the need for a bespoke "run migrations on exactly one designated pod" convention (a job name, a leader-election hack, a if [ "$HOSTNAME" = ... ] guard in an entrypoint). Consolidating that logic into the package replaces ad-hoc, per-team coordination with one tested path. The corollary is that the lock backend becomes a scaling dependency: under high pod-churn or aggressive rollout concurrency, lock contention and retry behavior become capacity considerations, especially for long-running migrations.
Failure modes to plan for:
migrate plus a leader-election guard in the entrypoint) is measured in hours, not weeks.Ramp-up: Minimal for engineers familiar with Laravel deploys. The learning cost is concentrated in the on-call/deploy-owner role: understanding the coordination primitive, what the logs look like when a host waits versus executes, and how to respond when a migration appears stuck behind a lock. Allocate roughly half a day of enablement for the platform team and write the runbook entry before production rollout.
Recommendation: Adopt, but pin the version and configure the execution mode explicitly rather than relying on the new default. The v0.3.5 change is a net positive for scaled environments, yet it is a silent semantic shift delivered in a patch release — which is exactly the profile of change that causes deployment incidents when consumed automatically. Gate production rollout on a successful concurrent-deploy test against the real lock backend and on a written answer to the fail-open/fail-closed question.
How can I help you explore Laravel packages today?