Deployer is the PHP world's answer to what Capistrano was for Ruby: a `deploy.php` recipe, atomic symlinked releases, before/after hooks, and a `dep deploy` command that runs over SSH. For most PHP projects — Laravel, Symfony, plain old framework-less PHP — it has been the default for years.

This guide is for teams that have outgrown the file-and-CLI model and want a web UI, an audit log of every deploy, role-based deploy permissions, one-click rollback, and a build pipeline that runs *off* the target server rather than piped through SSH on every release. Deployer is not deprecated and your `deploy.php` will keep working — but a hand-rolled recipe is no longer the only sensible answer for production PHP deployments.

For a side-by-side feature comparison, see [DeployHQ vs Deployer](https://www.deployhq.com/compare/deployhq-vs-deployer). If you are weighing alternatives across the PHP ecosystem, you may also want [DeployHQ vs Envoyer](https://www.deployhq.com/compare/deployhq-vs-envoyer). And if your `deploy.php` calls were already orchestrated from a CI pipeline rather than a human running `dep deploy`, [Migrating from GitHub Actions to DeployHQ](/support/migrating-to-deployhq/migrating-from-github-actions-to-deployhq) covers the hybrid pattern.

Coming from another script-based deploy tool? The migration shape for Capistrano is nearly identical — see [Migrating from Capistrano to DeployHQ](/support/migrating-to-deployhq/migrating-from-capistrano-to-deployhq).

## Feature parity: Deployer to DeployHQ

Everything `dep deploy` does, DeployHQ does. The configuration moves from a PHP file into a web UI; the deploy semantics stay the same.

| Deployer | DeployHQ |
| --- | --- |
| `deploy.php` | DeployHQ project (web-configured) |
| `host('prod.example.com')->set('deploy_path', '/var/www/app')` | Server entry with hostname, user, and deploy path |
| `set('repository', '...')` | Repository connection (Git, SVN, or Mercurial) |
| `set('branch', 'main')` | Branch per environment |
| `set('shared_files', [...])`, `set('shared_dirs', [...])` | Shared paths configured once per environment |
| Deployer release directory and `current` symlink | [Atomic deploy with automatic symlink swap](/support/deployments/zero-downtime-deployments/setting-up-zero-downtime-deployments) |
| `task('deploy', [...])` and the `deploy:*` lifecycle | Deploy lifecycle stages in the dashboard |
| `before()` / `after()` hooks | [SSH commands](/support/ssh-commands) tied to deploy lifecycle |
| `run('php artisan migrate')`, `run('composer install')` | SSH commands and [build pipeline](/support/build-pipelines) steps |
| `dep rollback` | [One-click rollback](/support/deployments/rolling-back-a-deployment) to any prior deploy |
| `set('keep_releases', 5)` | Configurable release retention per environment |
| `host(...)->stage('production')` (multi-stage) | Native multi-environment per project |
| Recipes (`recipe/laravel.php`, `recipe/symfony.php`) | Pre-canned build pipeline + SSH command sets you wire once |

## What DeployHQ adds that Deployer never did

| Capability | Why it matters |
| --- | --- |
| Web UI plus REST and GraphQL API | Deploy from a browser, a CLI, or a CI step. No `dep` binary, no PHP, and no `deploy.php` on the deploying machine. |
| Audit log of every deploy | Who deployed what, when, from which branch, with which commit hash. Deployer logs to your terminal scrollback. |
| [Build pipelines](/support/build-pipelines) | Run `composer install --no-dev --optimize-autoloader`, `npm ci && npm run build`, and asset builds *once* on our build infrastructure — not piped through `run()` on every target. Faster deploys, less server load. |
| AI deploy error explanation | When a deploy fails, the failed step is summarised in plain English with a likely fix. |
| [DeployHQ CLI](/support/cli) | `deployhq deploy production` from your terminal, scriptable in CI. |
| Per-environment permissions | Grant deploy-to-staging without granting deploy-to-production. Junior engineers can ship without root or repo write access. |
| Optional [Managed VPS](/support/managed-vps-hosting) (beta) | Provision a server inside DeployHQ if you do not already have one. |

## Migration in three steps

### 1. Connect your repository

Point DeployHQ at the same Git URL your `set('repository', ...)` line uses. Read-only deploy keys are generated automatically. DeployHQ checks out your code on our build infrastructure, so you do not need `git` on the target server.

### 2. Recreate your hosts as environments

Each `host()` definition in `deploy.php` becomes a server attached to a DeployHQ environment:

- `host('prod.example.com')->set('remote_user', 'deploy')` becomes the server's hostname and SSH user.
- `->set('deploy_path', '/var/www/app')` becomes the environment deploy path.
- `set('shared_files', ['.env'])` and `set('shared_dirs', ['storage', 'bootstrap/cache'])` become shared files and shared paths.
- `->set('branch', 'main')` becomes the branch per environment.

If your `deploy.php` defines multiple stages (`->stage('production')`, `->stage('staging')`), each stage maps to a separate DeployHQ environment. Multi-host stages — common in load-balanced setups — map to multiple servers attached to the same environment, deployed in parallel with the same atomic symlink swap.

### 3. Translate recipes and hooks into build pipeline + SSH commands

This is the bulk of the migration. Recipe steps (`deploy:vendors`, `artisan:migrate`, `artisan:storage:link`) and your custom `before()` / `after()` hooks become two kinds of DeployHQ artefact:

A typical Laravel `deploy.php` like this:

```php
namespace Deployer;
require 'recipe/laravel.php';

set('repository', 'git@github.com:acme/shop.git');

host('prod.example.com')
    ->set('remote_user', 'deploy')
    ->set('deploy_path', '/var/www/shop')
    ->set('branch', 'main');

after('deploy:vendors', 'artisan:migrate');
after('deploy:symlink', 'artisan:queue:restart');
after('deploy:symlink', 'artisan:horizon:terminate');
```

becomes, in DeployHQ:

- `composer install --no-dev --optimize-autoloader` — moved into the [build pipeline](/support/build-pipelines), so it runs once on our build server and the `vendor/` directory is uploaded with the release rather than installed on every target.
- `npm ci && npm run build` — also a build pipeline step, for the same reason.
- `php artisan migrate --force` — an SSH command set to run *after upload, before changing the symlink*.
- `php artisan storage:link` — an SSH command set to run on first deploy or as needed.
- `php artisan queue:restart` and `php artisan horizon:terminate` — SSH commands set to run *after deploy*.
- `php artisan config:cache && php artisan route:cache` — SSH commands set to run *after upload*.

Symfony, plain PHP, and custom recipes follow the same shape: anything that produces or installs files becomes a build pipeline step; anything that runs *on* the target after upload becomes an SSH command bound to the right deploy stage.

For most Deployer projects, end-to-end migration is a half-day. Concierge migration is available on Pro and above — send us your `deploy.php` and we will rebuild the project with you on a screenshare.

## What teams ask before they switch

**Will my Deployer recipes (Laravel, Symfony, custom) survive?** Yes. Recipes are just opinionated bundles of tasks and hooks. Each task that runs over SSH on the target moves to a DeployHQ SSH command; each task that produces artefacts (composer install, asset builds) moves to the build pipeline. Once wired, you never touch them again.

**Can I keep using my existing servers?** Yes. DeployHQ deploys to any SSH-reachable host. Bring your own VPS, dedicated server, or cloud instance.

**What about my custom `task()` blocks?** Anything that ran over SSH on the target server runs identically — it is just configured in the dashboard instead of `deploy.php`. Tasks that ran locally (e.g. building assets, packaging) move into the build pipeline.

**Do you support multi-host deploys?** Yes. Add multiple servers to one environment; DeployHQ deploys to all of them in parallel with the same atomic symlink swap.

**What if I want to roll back?** One click. DeployHQ keeps your last N releases on the server (you configure N). Rollback flips the symlink back. No `dep rollback` required.

**Can I still trigger from CI?** Yes — and this is often the cleanest setup. Keep your tests in GitHub Actions or GitLab CI and call `dhq deploy` as the deploy step. See [Migrating from GitHub Actions to DeployHQ](/support/migrating-to-deployhq/migrating-from-github-actions-to-deployhq) for the hybrid pattern.

**My team is more familiar with Envoyer.** Worth a look at [DeployHQ vs Envoyer](https://www.deployhq.com/compare/deployhq-vs-envoyer) before committing — Envoyer is Laravel-specific and managed; DeployHQ supports any stack and integrates with build pipelines on our infrastructure. If you are migrating off Envoyer rather than Deployer, see [Migrating from Envoyer to DeployHQ](/support/migrating-to-deployhq/migrating-from-envoyer-to-deployhq).

## Start your migration

Start a free DeployHQ project — connect your repo, point at one server, and deploy in under 15 minutes. No credit card.

If you also need a server, the [Managed VPS beta](/support/managed-vps-hosting) provisions one inside the same dashboard.

Deployer is a trademark of its respective maintainers. DeployHQ is not affiliated with the Deployer project. We just understand what graduating from a single-file `deploy.php` looks like.
