This guide walks you through running Capistrano 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 `bundle exec cap production deploy` from DeployHQ instead of from a developer laptop.

## Prerequisites

Before you begin, ensure you have:

* **A Capistrano-deployed Ruby application:** Your repository contains a `Capfile` and the relevant `config/deploy.rb` and stage files.
* **An SSH key authorised on the target hosts:** Capistrano 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.
* **`ed25519` and `bcrypt_pbkdf` gems in your Gemfile:** DeployHQ Project SSH keys are ed25519. The `net-ssh` library used by Capistrano does not support ed25519 keys without these two optional gems. Add them anywhere outside a development-only group:

  ```ruby
  gem "ed25519", "~> 1.2", require: false
  gem "bcrypt_pbkdf", "~> 1.0", require: false
  ```

  Without these, Capistrano will appear to "authenticate" then fail with `Net::SSH::AuthenticationFailed`, because it silently ignores the unsupported ed25519 key in the agent and finds no other usable key.
* **Known Hosts for each target server (optional but recommended):** 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 `cap` 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 **Ruby + Capistrano** from the template dropdown. This sets the Docker image to `ruby:3.3-bullseye`.
5. If your application targets a different Ruby version, override the image with a matching tag, for example `ruby:2.7.8-bullseye` or `ruby:3.2-slim`. Pick the tag that matches the `.ruby-version` file in your repository.
6. Enter your Capistrano commands in the **Commands** field. A typical configuration looks like this:

   ```text
   cd /data && bundle config set --local deployment 'true' && bundle install --without development test
   cd /data && bundle exec cap production deploy BRANCH=%branch% REVISION=%endrev%
   ```

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

## Adding Known Hosts

For every host Capistrano 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

Capistrano 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 Capistrano 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 Capistrano deploys. Rotating it rotates both. Plan rotations accordingly, or wait for the dedicated Deploy Key field in a future release.

## Example Configurations

### Standard Rails Deploy

```text
cd /data && bundle config set --local deployment 'true' && bundle install --without development test
cd /data && bundle exec cap production deploy
```

### Targeting a Specific Branch

```text
cd /data && bundle config set --local deployment 'true' && bundle install --without development test
cd /data && bundle exec cap production deploy BRANCH=%branch%
```

`%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 Capistrano stage:

* Server `Staging` with command `bundle exec cap staging deploy`
* Server `Production` with command `bundle exec cap production deploy`

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

### Skipping Asset Precompilation

If your Rails project precompiles assets via a separate build command, disable Capistrano's compile task by setting it in `deploy.rb`:

```ruby
set :assets_roles, []
```

## 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 Capistrano is trying to reach.

**"bundler: command not found":**

* The default `ruby:3.3-bullseye` image ships with Bundler. If you override the image, make sure your replacement also includes Bundler, or install it explicitly with `gem install bundler`.

**"Deploy timed out":**

* Custom Actions have a 30-minute overall timeout. Multi-host Capistrano deploys with asset precompilation 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 `bundle install` step.

**"Could not locate Gemfile.lock":**

* Capistrano-bundler expects a `Gemfile.lock` checked into the repository. Generate one locally with `bundle install`, commit, and redeploy.

**"Net::SSH::AuthenticationFailed" despite the key being authorised:**

* Almost always means `net-ssh` is silently dropping the ed25519 Project key. Add `ed25519` and `bcrypt_pbkdf` to the Gemfile (see Prerequisites) and redeploy.

**"is not yet checked out. Run `bundle install` first." for a github-sourced gem:**

* The build container's default bundler (2.5.x) has a known issue where it silently skips github-sourced gems during the first `bundle install`. Either commit a `Gemfile.lock` generated with bundler 2.6+ (the container will then auto-install the newer bundler), or swap the github source for the released rubygems version of the same gem.

**"wrong number of arguments (given 2, expected 1)" inside a `capistrano-*` plugin:**

* A third-party Capistrano plugin in your Gemfile is incompatible with `capistrano >= 3.18`. Pin the plugin to a compatible version, or comment out the plugin's `require` line in `Capfile` if you do not need it for the deploy.

## Related Articles

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