Devpilot
Deployments

Deployment Hooks

Learn how to configure deployment hooks in Devpilot — the commands and actions that run at each stage of the deployment lifecycle.

Deployment Hooks

Deployment hooks are the commands and file-sync actions that Devpilot executes during each stage of the deployment lifecycle. They define what happens when your code is deployed — from installing dependencies to restarting services. Every application in Devpilot has its own set of hooks that you configure to match your build and release process.

How hooks work

When you trigger a deployment, Devpilot moves through the deployment lifecycle in order: Pre Execution, Pre Source Control, Post Source Control, Post Setup, Post Release. At each stage, Devpilot runs the Active hooks assigned to that stage in priority order. If a hook fails (returns a non-zero exit code), the deployment stops and is marked as Failed. Hooks that never ran in that deployment are marked Skipped.

Each hook has:

  • A name — A short label that shows up in the deployment log.
  • A stage — When the hook runs in the lifecycle.
  • An action type — What the hook does (run a shell command, sync a file, or sync a directory).
  • Metadata — Stage-specific configuration, such as the command to run or the path to sync.
  • A priority — The position of the hook within its stage. Hooks run in ascending priority order.
  • A status — Either Active (runs during deployment) or Inactive (stored but skipped).

Hook stages explained

Devpilot's deployment lifecycle has five stages:

StageWhen it runsCommon uses
Pre ExecutionBefore anything runs in the deployment.Pre-flight checks, "deployment starting" notifications, preparing server paths.
Pre Source ControlRight before Devpilot fetches your code.Last-minute branch checks, pausing traffic, announcing the upcoming release.
Post Source ControlRight after the repository has been fetched into a new release directory.Installing Composer dependencies, syncing environment or vendor files, copying shared assets, running tests.
Post SetupAfter the app's runtime is set up (dependencies installed, assets built).Running migrations and seeders, clearing and rebuilding framework caches, syncing storage or public directories, setting folder permissions.
Post ReleaseAfter the new release is made live.Restarting queue workers, reloading Docker containers, running post-release migrations, restarting the app.

Hook action types

Devpilot supports three action types for hooks:

Shell Command

Runs a shell command on the server. This is the most common action type. The command executes in a bash shell in the context of the current release directory. You enter the command in the hook's Command field.

Examples seen across live templates:

composer install --no-interaction --no-dev
php artisan migrate --force
php artisan optimize
npm install
npm run build
php artisan horizon:terminate
php artisan cache:clear

Sync File

Persists a single file across releases and symlinks it into each new release. Use this for configuration files, environment files, or a SQLite database that shouldn't be committed to the repository. Configure it with the file's relative path (for example .env) in the hook's Path field, plus optional Rsync Flags (defaults to -az).

This hook seeds the persisted copy only once: the first time it runs for an app, if no persisted copy exists yet, Devpilot copies the file out of the release into a persistent location at the same relative path under the app's root. On every deployment after that, the persisted copy is left untouched — Devpilot just removes the file from the new release and symlinks it back to the persisted copy. Edits made to the file on the server (or written by the running app) survive every future deployment; the version in your repository only ever seeds the first copy.

Sync Directory

Persists a directory across releases and symlinks it into each new release. Use this for directories that should survive redeployment, such as storage, vendor, public, or an uploads folder — it avoids re-installing large dependency trees and preserves user-generated files. Configure it with the directory's relative path in the hook's Path field, plus optional Rsync Flags (defaults to -r -t -O -v -c -S -u -q --no-times).

Unlike Sync File, this hook runs on every deployment, and it merges rather than overwrites. Each run:

  1. Copies (via rsync) the new release's version of the directory into the persistent location at the same relative path under the app's root. This is a merge: files that exist only in the persisted copy are left alone (there is no delete step), files new to this release are added, and files present in both are only replaced if the release's version is checksum-different and not older than the persisted one.
  2. Removes the now-redundant copy from the release.
  3. Symlinks the release path to the persistent directory, so the running app reads and writes the persisted directory directly from that point on.

Because existing files are never deleted from the persisted copy, removing a file from your repository will not remove it from the server — clean up stale files on the server directly if you need that.

Creating hooks with the visual editor

Open the hooks editor

Navigate to your application and open the Deployment Hooks tab.

Select a stage

The editor groups hooks by stage: Pre Execution, Pre Source Control, Post Source Control, Post Setup, and Post Release. Click Add Hook under the stage where the hook should run.

Configure the hook

  1. Enter a Name for the hook (this appears in deployment logs).
  2. Select the Action Type: Shell Command, Sync File, or Sync Directory.
  3. For Shell Command, enter the command.
  4. For Sync File or Sync Directory, enter the relative path to sync, and optionally custom rsync flags.
  5. Leave the hook Active to include it in deployments, or switch it to Inactive to keep the definition without running it.

Order your hooks

Reorder the hooks within each stage to set their priority. Hooks within the same stage run sequentially in ascending priority order.

Save

Save your hooks. The changes apply to the next deployment.

Example configurations

These examples mirror the hook patterns used by Devpilot's built-in Laravel template and by real production applications deployed through the platform.

Laravel application

  • Post Source Control
    • Shell Command — "Install Composer Dependencies": composer install --no-interaction --no-dev
  • Post Setup
    • Sync Directory — "Sync Storage Directory" with path storage
    • Shell Command — "Run migration": php artisan migrate --force
    • Shell Command — "Artisan optimize": php artisan optimize
    • Shell Command — "Storage link": php artisan storage:link
  • Post Release
    • Shell Command — "Restart Horizon": php artisan horizon:terminate

Node.js / JavaScript application

  • Post Source Control
    • Shell Command — "Install NPM dependencies": npm install
  • Post Setup
    • Shell Command — "Build": npm run build
  • Post Release
    • Shell Command — "Restart app": pm2 restart ecosystem.config.js

Adding persistent directories

For any application, configure a Sync Directory hook in Post Setup for directories that should survive across releases — uploads, generated files, caches, or a shared vendor folder. This is how Devpilot templates handle paths like storage, public, and vendor on Laravel apps.

Best practices

  • Keep hooks focused. Each hook should do one thing. Instead of chaining multiple commands with && in a single hook, create separate hooks so each runs with its own status, duration, and exit code in the deployment log.
  • Use meaningful names. Hook names appear in every deployment log. Clear names like "Install Composer Dependencies" or "Run migration" make logs easier to scan.
  • Keep definitions but pause them when needed. Switch a hook from Active to Inactive to skip it temporarily without deleting the configuration.
  • Start from a template. If you have a hook configuration that works, save it as a pipeline template to reuse across applications.
  • Test commands on the server first. Before promoting a command to a hook, run it manually on the server via the Web Terminal to verify it works in the deployment environment.