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
Capfileand the relevantconfig/deploy.rband 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_keysfile of the deploy user on every target server. ed25519andbcrypt_pbkdfgems in your Gemfile: DeployHQ Project SSH keys are ed25519. Thenet-sshlibrary 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
- Open your project in DeployHQ and navigate to Servers.
- Click New Server and select Custom Action as the protocol.
- Under Image Source, select Curated Template.
- Choose Ruby + Capistrano from the template dropdown. This sets the Docker image to
ruby:3.3-bullseye. - If your application targets a different Ruby version, override the image with a matching tag, for example
ruby:2.7.8-bullseyeorruby:3.2-slim. Pick the tag that matches the.ruby-versionfile in your repository. - 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%
- Leave Halt on error enabled so the deployment is marked failed if Capistrano returns a non-zero exit code.
- 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:
- In DeployHQ, open Project -> Repository and copy the Public Key shown on that page.
- On each target server, append it to
~deploy/.ssh/authorized_keys(or whichever user Capistrano uses). - 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
Stagingwith commandbundle exec cap staging deploy - Server
Productionwith commandbundle 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_keyson every target host. - Run
ssh-add -las 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-bullseyeimage ships with Bundler. If you override the image, make sure your replacement also includes Bundler, or install it explicitly withgem 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 installstep.
"Could not locate Gemfile.lock":
- Capistrano-bundler expects a
Gemfile.lockchecked into the repository. Generate one locally withbundle install, commit, and redeploy.
"Net::SSH::AuthenticationFailed" despite the key being authorised:
- Almost always means
net-sshis silently dropping the ed25519 Project key. Added25519andbcrypt_pbkdfto 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 aGemfile.lockgenerated 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'srequireline inCapfileif you do not need it for the deploy.