This guide walks you through running [Deployer](https://deployer.org/) deploys with DeployHQ's [Custom Action](Article: custom-action) protocol. Custom Actions execute commands inside a Docker container during your deployment pipeline, so you can run `vendor/bin/dep deploy` from DeployHQ instead of from a developer laptop.

## Prerequisites

Before you begin, ensure you have:

* **A Deployer-managed PHP application:** Your repository contains a `deploy.php` (or `deploy.yaml`) recipe and lists `deployer/deployer` in `composer.json`.
* **An SSH key authorised on the target hosts:** Deployer connects from the container to your production servers over SSH. The container's ssh-agent is populated with your **Project SSH key**, so that key's public half must appear in the `authorized_keys` file of the deploy user on every target server.
* **Known Hosts for each target server:** Configured under **Project -> Build Configurations -> Known Hosts**. Without these, strict host key checking is disabled, which is fine for an initial dry run but should be enabled before going to production.

## How SSH Works Inside a Custom Action

When a Custom Action runs, DeployHQ loads two private keys into an ssh-agent on the build server and mounts the agent socket into the container at `/tmp/ssh_agent_socket`:

* Your **Project SSH key** (the same one used to clone the repository from your SCM provider).
* The DeployHQ default key (used internally).

The private keys are never written to the container's filesystem. The container only receives the agent socket, so your commands can call `ssh`, `git`, or `dep` and have authentication work transparently, but cannot read the key material directly.

Any **Known Hosts** configured on the project are written to a `known_hosts` file inside the container. If at least one Known Host exists, strict host key checking is enabled automatically.

## Setting Up the Server

1. Open your project in DeployHQ and navigate to **Servers**.
2. Click **New Server** and select **Custom Action** as the protocol.
3. Under **Image Source**, select **Curated Template**.
4. Choose **PHP + Deployer** from the template dropdown. This sets the Docker image to `php:8.3-cli`.
5. If your application targets a different PHP version, override the image with a matching tag, for example `php:8.2-cli` or `php:7.4-cli`.
6. Enter your Deployer commands in the **Commands** field. A typical configuration looks like this:

   ```text
   cd /data && composer install --no-dev --optimize-autoloader
   cd /data && vendor/bin/dep deploy production --revision=%endrev%
   ```

7. Leave **Halt on error** enabled so the deployment is marked failed if Deployer returns a non-zero exit code.
8. Click **Create Server**.

## Adding Known Hosts

For every host Deployer will connect to, capture its SSH host key and add it to the project's Known Hosts:

```text
ssh-keyscan -H app-01.example.com >> known_hosts.txt
```

Open the contents of `known_hosts.txt`, paste the line into **Project -> Build Configurations -> Known Hosts**, and save. Repeat for each target server. DeployHQ enables strict host key checking automatically once any Known Hosts are configured.

## Authorising the Deploy Key

Deployer needs the deploy user on each target host to accept connections from the Project SSH key:

1. In DeployHQ, open **Project -> Repository** and copy the **Public Key** shown on that page.
2. On each target server, append it to `~deploy/.ssh/authorized_keys` (or whichever user Deployer uses).
3. Verify by running `ssh deploy@app-01.example.com 'echo ok'` from a server with access to the key. Authentication should succeed without a prompt.

The Project SSH key is shared between SCM access and Deployer deploys. Rotating it rotates both. Plan rotations accordingly.

## Example Configurations

### Standard Laravel Deploy

```text
cd /data && composer install --no-dev --optimize-autoloader
cd /data && vendor/bin/dep deploy production
```

### Targeting a Specific Branch

```text
cd /data && composer install --no-dev --optimize-autoloader
cd /data && vendor/bin/dep deploy production --branch=%branch% --revision=%endrev%
```

`%branch%` and `%endrev%` are expanded by DeployHQ before the command runs. The full list of supported variables is in the [text variables](Article: #165) reference.

### Multi-Stage Deploys

Create one Custom Action server per stage and set its name to match your Deployer host alias:

* Server `Staging` with command `vendor/bin/dep deploy staging`
* Server `Production` with command `vendor/bin/dep deploy production`

Deploy each server from its own auto-deploy branch to wire up environment-specific releases.

### Installing PHP Extensions

The default `php:8.3-cli` image is minimal. If your `composer.json` requires extensions such as `pdo_mysql`, `gd`, or `intl`, add an install step before composer runs:

```text
docker-php-ext-install pdo_mysql
cd /data && composer install --no-dev --optimize-autoloader
cd /data && vendor/bin/dep deploy production
```

For repeated deploys, save build time by baking required extensions into a custom Docker image and selecting it under **Image Source -> Custom Docker Image**.

## Troubleshooting

**"Permission denied (publickey)":**

* Confirm the Project SSH public key is present in the deploy user's `authorized_keys` on every target host.
* Run `ssh-add -l` as the first command in your Custom Action to verify the key is loaded inside the container.

**"Host key verification failed":**

* Strict host key checking is enabled because at least one Known Host is configured. Add a Known Host entry for the host Deployer is trying to reach.

**"Class 'Deployer\\Deployer' not found":**

* Run `composer install` before invoking `dep`. The default template does this in the first command line.

**"Deploy timed out":**

* Custom Actions have a 30-minute overall timeout. Multi-host Deployer recipes with asset builds can approach this limit. Move asset compilation into a [build command](Article: build-commands) before the Custom Action runs, or pre-bake your dependencies into a custom Docker image to skip the `composer install` step.

**"docker-php-ext-install: command not found":**

* You overrode the image with one that is not based on the official `php` image. Either revert to the official tag or install extensions using the new image's package manager.

## Related Articles

* [Custom Actions](Article: custom-action)
* [Configuring SSH Known Hosts](Article: build-known-hosts)
* [Build Commands](Article: build-commands)
