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::Pumainto your Capfile afterrequire '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.0allows>= 3.0, < 4.0~> 3.1allows>= 3.1, < 4.0~> 3.1.2allows>= 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
- Always pin major versions in your Gemfile with
~>constraints - Never delete Gemfile.lock โ it’s your reproducibility guarantee
- Update dependencies deliberately, not accidentally via
bundle - Read changelogs before major version upgrades
- Test in staging before deploying to production
- Extract precise search queries when debugging โ remove noise, keep specifics
Comments