Skip to main content
Laravel, shipping fast.
Chapter 14 ยท DevOps & Infrastructure

Provisioning as something you can repeat

Julian Beaujardin

Provisioning a server by hand, once, carefully, is easy. Doing it the same way the fifth time, at 11pm, under pressure, without silently skipping the step you always skip, is where hand-provisioning actually fails.

This book has spent twelve chapters arguing for one idea applied to license lookups: don't hand-roll a call to an external system inline, put a Manager behind a Facade, put a Driver behind the Manager, and let the rest of your codebase depend on the interface instead of the provider. LicenseManager doesn't know or care that it's Statamic underneath. It just knows how to hand back a license.

Provisioning deserves the same treatment, for the same reason. A server provisioner that's a case in an enum, Forge, right next to GitHub and Stripe, is a provisioner your queue jobs can depend on, retry against, and report failures for, using the exact same machinery this book already built for retries, backoff, and encrypted job payloads in earlier chapters. A provisioner that's a shell script someone runs from their own terminal, remembering the flags from memory, is a provisioner that fails differently every time someone new runs it.

Repeatable provisioning isn't a separate discipline from the patterns in this book. It's the same discipline, DTOs for what the provider needs to know, a typed contract for what it hands back, a Manager that hides which vendor is underneath, applied to servers instead of licenses.

Chapter 14 Summary

Before you deploy

  • [X] CI runs the same format, check, and test commands a developer runs locally, and fails the build if any of them do
  • [X] Releases build in an isolated directory and swap in atomically, so no request is ever served by a half-updated codebase
  • [X] Migrations are additive and guard themselves with Schema::hasTable() / Schema::hasColumn(), safe to run twice
  • [X] Secrets exist only as env() calls inside config files, never inline in application code, and never inside .env in the repository

When something goes wrong

  • [X] Roll the code back first, by swapping the release symlink, before touching the schema at all
  • [X] Treat migrate:rollback as a last resort, not a first response, since a down() method can delete data your rollback was never meant to touch
  • [X] Restart queue workers after every deploy, so no worker keeps running job classes from the release you just replaced

A deploy nobody notices is a deploy that worked. That's not a low bar. It's the whole job.