Deploy Steps
A project’s deploy pipeline is an ordered list of steps that run against every configured server (or a subset, if a step targets specific servers). There are two kinds of step:
- Builtin steps (
clone,composer,activate,purge) implement the core mechanics of a zero-downtime deploy: extracting the release, symlinking shared storage, atomically activating, and cleaning up old releases. - Custom steps run a shell script you write, over SSH, in the release directory (or wherever you point it with placeholders).
Default pipeline
Section titled “Default pipeline”New projects are seeded with a bare-minimum, zero-downtime pipeline:
| Order | Step | Type |
|---|---|---|
| 10 | Clone New Release | builtin (clone) |
| 20 | Activate New Release | builtin (activate) |
| 30 | Purge Old Releases | builtin (purge) |
This alone is a working pipeline: it downloads, extracts, and activates your code. For a real Laravel app you’ll almost always add custom steps for Composer, migrations, cache warming, and queue restarts. Add them from Add Custom Step, using the predefined library below to prefill common ones, and drag them into place between Clone and Activate (or after Activate, for anything that should run against the live release, like queue restarts).
Steps run strictly in sort order, sequentially, across all target servers before moving to the next step.
Editing the pipeline
Section titled “Editing the pipeline”- Reordering: drag any step (builtin or custom) by its handle on the Deploy Steps tab; the new order is saved immediately.
- Editing and deleting: only custom steps can be edited or removed. The four builtin steps (
clone,composer,activate,purge) can be reordered but not edited or deleted through the UI, since they implement the mechanics the rest of the pipeline depends on. - Adding: click Add Custom Step. You can start from a predefined command (Composer, npm build, migrate,
storage:link, Laravel optimize, Filament optimize, restart Horizon, restart queue workers) which prefills the name and script, or write your own from scratch.
Placeholders
Section titled “Placeholders”Custom step scripts can use these placeholders, expanded at deploy time:
| Placeholder | Expands to |
|---|---|
{{release}} |
This deployment’s release directory, e.g. /home/website/project.com/releases/20251018132019 |
{{current}} |
The live current symlink, e.g. /home/website/project.com/current |
{{deploy_path}} |
The base deploy path, e.g. /home/website/project.com |
cd {{release}} && php artisan migrate --forcecd {{current}} && php artisan horizon:terminate{{current}} is useful for commands that should always act on whatever release is currently live (like restarting queue workers or Horizon), as opposed to {{release}}, which is the release being deployed right now. The two only differ during the deploy itself, before activation.
Run As
Section titled “Run As”Every step, builtin or custom, has an optional Run As field. When set, the step’s command runs via non-interactive sudo as that user:
sudo -n -u deploy bash -c '<your command>'Leave it blank to run as the SSH user you connect with. Using Run As requires the SSH user to have passwordless sudo configured for that target user on the server.
Target servers
Section titled “Target servers”Both builtin steps that touch the filesystem and custom steps can be scoped to a subset of servers (leave unchecked to run on all servers). This is useful for steps that should only run once, like queue restarts on a dedicated worker box.
Why migrations run before activation
Section titled “Why migrations run before activation”The clone step symlinks the shared .env and storage into the new release directory immediately after extraction, not at activation time. That means php artisan commands that need config, a database connection, or writable storage (migrations, cache warming) can run safely against the new release before current is swapped to point at it. The activate step re-links .env/storage too, but by then it’s just an idempotent safety net.
Queue workers are restarted after activation, so they pick up the newly-live code from current rather than the release that’s about to be replaced.
See How Deployments Work for the full six-phase flow and the on-server directory layout.