Last updated on 28th May 2026

Deploy Ruby Applications with Capistrano

This guide walks you through running Capistrano deploys with DeployHQ's 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:
  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:
   cd /data && bundle config set --local deployment 'true' && bundle install --without development test
   cd /data && bundle exec cap production deploy BRANCH=%branch% REVISION=%endrev%
  1. Leave Halt on error enabled so the deployment is marked failed if Capistrano returns a non-zero exit code.
  2. 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:

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

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

Targeting a Specific Branch

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 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:

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 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.