Skip to main content
โšก Calmops

Debugging Capistrano Deploy Failures: Lessons in Dependency Management

The Problem

A Rails deployment that had been working fine suddenly failed with no changes to the Capistrano configuration:

$ cap production deploy

(Backtrace restricted to imported tasks)
cap aborted!
Don't know how to build task 'start' (see --tasks)

Tasks: TOP => production
(See full trace by running task with --trace)

The last successful deploy was a week earlier. No Capistrano-related code had been touched. Yet the deploy was completely broken.

Debugging Process

Step 1: Add –trace for More Detail

cap production deploy --trace

The trace output was verbose but didn’t immediately reveal the root cause. Time to search.

Step 2: Extract the Right Search Query

The key to effective debugging searches is extracting the most specific, unique part of the error message and removing noise:

Original error: Don't know how to build task 'start' (see --tasks)

Search query: capistrano "Don't know how to build task 'start'"

Remove:

  • Generic words (“see”, “tasks”)
  • Special characters that confuse search engines
  • Add the main technology name (capistrano)

The third result on Stack Overflow had the answer:

Add install_plugin Capistrano::Puma into your Capfile after require 'capistrano/puma'.

capistrano3-puma moved to 3.0 a few days ago. This line is required for loading default puma tasks in this version.

The Fix

Add one line to Capfile:

# Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git

require 'capistrano/puma'
install_plugin Capistrano::Puma  # โ† this line was required in v3.0+

# Load custom tasks
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Then run bundle and deploy again:

bundle install
cap production deploy
# โœ“ Success

Root Cause Analysis

What Actually Happened

The Gemfile had capistrano-puma without a strict version constraint:

# Gemfile โ€” before fix
gem 'capistrano-puma'  # no version constraint!

When bundle was run (or Gemfile.lock was deleted), Bundler fetched the latest version โ€” which had just released v3.0 with a breaking change. The new version required install_plugin Capistrano::Puma in the Capfile, but the old configuration didn’t have it.

The Semantic Versioning Contract

Semantic versioning defines version numbers as MAJOR.MINOR.PATCH:

  • PATCH (1.0.x): Bug fixes โ€” backward compatible
  • MINOR (1.x.0): New features โ€” backward compatible
  • MAJOR (x.0.0): Breaking changes โ€” not backward compatible

A jump from 2.x to 3.0 is a major version bump โ€” it signals breaking changes. The gem maintainers followed semver correctly; the problem was that the Gemfile didn’t constrain the version.

Preventing This in the Future

1. Lock Major Versions in Gemfile

# Gemfile โ€” after fix
gem 'capistrano-puma', '~> 3.0'  # allows 3.x but not 4.0

The ~> (pessimistic constraint) operator:

  • ~> 3.0 allows >= 3.0, < 4.0
  • ~> 3.1 allows >= 3.1, < 4.0
  • ~> 3.1.2 allows >= 3.1.2, < 3.2.0

2. Commit and Never Delete Gemfile.lock

Gemfile.lock pins every gem to an exact version. Deleting it forces Bundler to resolve all dependencies fresh โ€” potentially pulling in breaking changes.

# Always commit Gemfile.lock
git add Gemfile.lock
git commit -m "Update Gemfile.lock"

# Never do this without understanding the consequences:
rm Gemfile.lock  # dangerous!

3. Update Dependencies Deliberately

# Update a specific gem (safer than updating everything)
bundle update capistrano-puma

# Update all gems (risky โ€” review the diff carefully)
bundle update

# Check what would change before updating
bundle outdated

4. Read Changelogs Before Major Updates

Before updating a gem to a new major version, read its CHANGELOG or release notes:

# Check current version
bundle show capistrano-puma

# View gem info
gem info capistrano-puma

# Check GitHub releases page for breaking changes
# https://github.com/seuros/capistrano-puma/releases

5. Test Deploys in Staging First

# Deploy to staging before production
cap staging deploy

# If staging works, deploy to production
cap production deploy

Capistrano Configuration Reference

A complete, working Capfile for a Rails + Puma + Nginx setup:

# Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git

require 'capistrano/rails'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Nginx  # if using nginx integration

Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
# config/deploy.rb
lock '~> 3.17'

set :application, 'myapp'
set :repo_url,    '[email protected]:username/myapp.git'
set :branch,      :main
set :deploy_to,   '/var/www/myapp'

set :linked_files, %w[config/database.yml config/master.key .env]
set :linked_dirs,  %w[log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system]

set :keep_releases, 5
set :puma_threads,  [4, 16]
set :puma_workers,  2

Key Lessons

  1. Always pin major versions in your Gemfile with ~> constraints
  2. Never delete Gemfile.lock โ€” it’s your reproducibility guarantee
  3. Update dependencies deliberately, not accidentally via bundle
  4. Read changelogs before major version upgrades
  5. Test in staging before deploying to production
  6. Extract precise search queries when debugging โ€” remove noise, keep specifics

Resources

Comments