capistrano/capistrano
A deployment automation tool built on Ruby, Rake, and SSH.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1w ago
- ✓26+ active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 57% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/capistrano/capistrano)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/capistrano/capistrano on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: capistrano/capistrano
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/capistrano/capistrano shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1w ago
- 26+ active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 57% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live capistrano/capistrano
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/capistrano/capistrano.
What it runs against: a local clone of capistrano/capistrano — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in capistrano/capistrano | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 37 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of capistrano/capistrano. If you don't
# have one yet, run these first:
#
# git clone https://github.com/capistrano/capistrano.git
# cd capistrano
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of capistrano/capistrano and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "capistrano/capistrano(\\.git)?\\b" \\
&& ok "origin remote is capistrano/capistrano" \\
|| miss "origin remote is not capistrano/capistrano (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "bin/cap" \\
&& ok "bin/cap" \\
|| miss "missing critical file: bin/cap"
test -f "capistrano.gemspec" \\
&& ok "capistrano.gemspec" \\
|| miss "missing critical file: capistrano.gemspec"
test -f "Rakefile" \\
&& ok "Rakefile" \\
|| miss "missing critical file: Rakefile"
test -f "DEVELOPMENT.md" \\
&& ok "DEVELOPMENT.md" \\
|| miss "missing critical file: DEVELOPMENT.md"
test -f "CONTRIBUTING.md" \\
&& ok "CONTRIBUTING.md" \\
|| miss "missing critical file: CONTRIBUTING.md"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 37 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~7d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/capistrano/capistrano"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Capistrano is a Ruby-based deployment automation framework that executes Rake tasks over SSH to orchestrate application deployments across multiple servers. It eliminates manual deployment steps by defining a standardized process (similar to Ansible or Fabric) and provides parallel execution across server fleets, making it the industry standard for Rails deployments since 2006. Modular gem structure: bin/cap and bin/capify are CLI entry points, capistrano.gemspec defines the package boundary, lib/ contains the core rake-based task runner and SSH orchestration, spec/ holds RSpec tests, and features/ contains Gherkin integration tests. The docker-compose.yml and .docker/ provide isolated test environments mimicking real deployment targets.
👥Who it's for
DevOps engineers and Ruby developers building Rails applications who need to automate reproducible deployments to Linux/Unix servers without container orchestration. Users typically manage their own infrastructure and want SSH-native, code-first deployment definitions.
🌱Maturity & risk
Capistrano is production-ready and actively maintained. The codebase shows 221,020 lines of Ruby with comprehensive test coverage (Gherkin BDD tests present), CI/CD via GitHub Actions (.github/workflows/ci.yml), a detailed CHANGELOG.md, and the RELEASING.md process indicates coordinated releases. Version 3.x is the current stable branch with clear upgrade paths (UPGRADING-3.7.md).
Risk is low to moderate: it's a mature, single-purpose tool with no node_modules-style dependency explosion visible, but the codebase depends heavily on SSH stability and correct server state. The 2006 project age means it's battle-tested but faces competition from containerized alternatives (Kubernetes, Docker Compose). Maintenance appears healthy via GitHub Actions CI, but single-language (Ruby) dominance means fewer modern language bindings.
Active areas of work
The project is maintaining version 3.x stability with dependabot monitoring (dependabot.yml), release-drafter automation (.github/workflows/release-drafter.yml), and an active CI pipeline (ci.yml). Recent work likely focuses on dependency updates and bug fixes rather than major feature additions, as evidenced by RELEASING.md's mature release process.
🚀Get running
git clone https://github.com/capistrano/capistrano.git && cd capistrano && bundle install (Gemfile present) && bundle exec rake (Rakefile present for task automation) && bundle exec rspec spec/ to run unit tests or bundle exec cucumber features/ for integration tests.
Daily commands: For development: bundle install, then bundle exec rake test (runs all tests via Rakefile). For CLI testing: bundle exec ./bin/cap --version. For integration tests in isolation: docker-compose up to spin up SSH-enabled test containers, then bundle exec cucumber features/
🗺️Map of the codebase
bin/cap— Entry point executable for the Capistrano CLI that all users invoke to trigger deployments.capistrano.gemspec— Gem specification defining Capistrano's dependencies and metadata, essential for understanding the project's external requirements.Rakefile— Root Rake task definitions that orchestrate the core deployment workflow and task execution model.DEVELOPMENT.md— Developer setup and contribution guidelines explaining how to build and test changes to the codebase.CONTRIBUTING.md— Contribution standards and code review expectations for submitting pull requests to the project.README.md— High-level overview of Capistrano's purpose, architecture, and basic usage patterns for all contributors.
🧩Components & responsibilities
- Capistrano CLI (Ruby, Rake, Thor (for option parsing)) — Entry point that loads configuration, resolves task dependencies, and orchestrates deployment lifecycle
- Failure mode: Exits with non-zero code and prints error; previous deployment state may be partially applied if rollback task fails
- SSH Connection Pool (Net::SSH, OpenSSH (client-side)) — Manages persistent SSH connections to target servers, handles reconnection/timeouts, and multiplexes concurrent commands
- Failure mode: Connection timeout or authentication failure halts task execution; no auto-retry by default
- Task DSL & Role System (Rake, Ruby metaprogramming) — Provides Ruby API for defining tasks, roles, and server assignments; executes tasks against filtered server sets
- Failure mode: Task failure on any server aborts remaining tasks unless rescue/on_error handlers defined; partial deployment state remains
- SCM Strategy (Git, Mercurial, shell execution) — Abstracts version control operations (clone, fetch, update) for multiple SCM types (Git, Mercurial)
- Failure mode: SCM checkout failure prevents code sync; deployment cannot proceed
🔀Data flow
Developer→cap CLI— Invokes cap command with stage and task (e.g., cap production deploy)cap CLI→Capfile— Loads configuration file defining servers, roles, and custom tasksCapfile→SSH Connection Pool— Role definitions trigger SSH connections to target serversSSH Connection Pool→Target Servers— Sends shell commands to remote systems for
🛠️How to make changes
Add a new Capistrano task for a deployment stage
- Define the Rake task in a new file under lib/capistrano/tasks/ following naming convention task_name.rake (
lib/capistrano/tasks/*.rake) - Implement the task using Capistrano DSL with on/roles blocks to target specific servers (
lib/capistrano/tasks/*.rake) - Require the task file in the main load path so it's automatically discovered when cap runs (
lib/capistrano.rb or equivalent loader) - Add test coverage in spec/ directory following the naming pattern spec/integration/*_spec.rb (
spec/integration/*_spec.rb)
Extend Capistrano with a custom SCM (Source Control Management) integration
- Create a new SCM strategy class that inherits from Capistrano::SCM in lib/capistrano/scm/ (
lib/capistrano/scm/custom_scm.rb) - Implement required methods: check, clone, fetch_revision, and update following the SCM interface contract (
lib/capistrano/scm/custom_scm.rb) - Register the SCM with Capistrano by adding it to the load path in an initializer (
lib/capistrano/scm.rb or equivalent registry) - Document the custom SCM in docs/documentation/advanced-features/custom-scm/index.markdown (
docs/documentation/advanced-features/custom-scm/index.markdown)
Create a deployment role filter for selective task execution
- Define a new filter class in lib/capistrano/dsl/filters/ that implements the filter interface (
lib/capistrano/dsl/filters/custom_filter.rb) - Implement the filter logic to accept/reject hosts based on custom role or property matching (
lib/capistrano/dsl/filters/custom_filter.rb) - Register the filter in the global Capistrano configuration loader (
lib/capistrano.rb or equivalent configuration) - Add usage examples and documentation to docs/documentation/advanced-features/filtering/index.markdown (
docs/documentation/advanced-features/filtering/index.markdown)
🔧Why these technologies
- Ruby & Rake — Provides a familiar task-based DSL for developers to define sequential, idempotent deployment steps; Rake is the standard Ruby task runner
- SSH — Agentless remote command execution without requiring additional server-side daemons; industry-standard for server automation
- Net::SSH — Pure Ruby SSH implementation enabling cross-platform deployment without OpenSSH dependency
⚖️Trade-offs already made
-
Role-based server targeting with on/roles DSL
- Why: Enables concise definition of which tasks run on which servers without hardcoding IPs
- Consequence: Requires upfront role planning in deploy.rb; adds abstraction layer but reduces per-server configuration
-
SSH connection pooling with parallel execution
- Why: Improves performance by reusing connections and executing tasks concurrently across servers
- Consequence: Introduces complexity in output interleaving and error handling across parallel streams; harder to debug linear task sequences
-
Task-based DSL on top of Rake
- Why: Leverages existing Ruby ecosystem knowledge and Rake's dependency resolution
- Consequence: Limited to synchronous Rake task model; no native async/await primitives for long-running operations
🚫Non-goals (don't propose these)
- Does not provide application-level deployment logic; users must write custom tasks for their framework (Rails, Java, etc.)
- Does not include secrets management; relies on external tools (Vault, AWS Secrets Manager, environment variables)
- Does not handle real-time deployment monitoring or metrics collection; delegates to external monitoring tools
- Does not support Windows native execution; requires WSL, Git Bash, or Cygwin on Windows clients
🪤Traps & gotchas
SSH key management: .docker/ssh_key_rsa is test-only and must never be committed to production (excluded in .gitignore). Test environments require Docker Compose and SSH daemon availability (see docker-compose.yml and .docker/ubuntu_setup.sh). Gherkin features/ tests are integration tests requiring external I/O and are slower than unit tests; run rspec spec/ for fast feedback. Role/stage scope is implicit in task definitions—mixing :all role with stage-specific variables can cause subtle deployment target mismatches. The bin/cap CLI expects Capfile in the current directory; tests may fail if run from wrong working directory.
🏗️Architecture
💡Concepts to learn
- Role-based server targeting — Capistrano's core abstraction—tasks run on subsets of servers via role filtering (on roles(:web), on roles(:db)); understanding role scope prevents tasks from running on wrong servers
- Rake task dependency graphs — Capistrano deployments are Rake tasks with before/after hooks and dependencies; understanding task ordering prevents deployment order bugs and circular dependencies
- Parallel SSH connection pooling — lib/capistrano/ssh.rb manages concurrent connections to avoid N sequential deploys; understanding pool limits and timeout tuning is critical for large fleets
- Stage-based configuration inheritance — Capistrano's unique strength: defining deploy once in Capfile, then overriding stage vars (config/deploy/production.rb vs staging.rb); misunderstanding scope causes ENV leaks
- Idempotency and failed deployment recovery — Unlike imperative scripts, Capistrano tasks should be re-runnable without side effects; the rollback mechanism depends on this, and users encounter this when SSH interrupts mid-deploy
- Remote command execution via heredocs and quoting — Capistrano's execute() method sends shell commands over SSH; shell escaping, variable expansion, and multiline commands are subtle and cause deployment failures if misunderstood
- Shared release directory and symlink swapping — Standard Capistrano workflow uses releases/ for per-deployment code copies and current/ symlink for active code; zero-downtime deploys depend on atomic symlink swaps, visible in lib/capistrano/tasks/
🔗Related repos
ruby/rake— Capistrano's task engine; understanding Rake is essential since Capistrano is a task runner on top of itruby/net-ssh— Underlying SSH client library (wrapped in lib/capistrano/ssh.rb); Capistrano's entire remote execution depends on thischef/chef— Alternative infrastructure automation tool using Ruby; Capistrano users often choose between Capistrano and Chef based on idempotence requirementsansible/ansible— Python-based competitor solving the same deployment problem without requiring Ruby; de facto modern replacement for Capistrano in many organizationscapistrano/capistrano-rails— Official companion gem providing Rails-specific deployment tasks; typical Capistrano user adds this to Gemfile
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for Docker-based deployment scenarios
The repo has Docker setup files (.docker/Dockerfile, .docker/ubuntu_setup.sh, docker-compose.yml) and SSH keys configured, but there's no corresponding test workflow in .github/workflows/ to validate Capistrano deployments in containerized environments. This would catch regression bugs in SSH connectivity, file transfers, and remote command execution that are critical to Capistrano's core functionality.
- [ ] Create .github/workflows/docker-integration-tests.yml that spins up containers using docker-compose.yml
- [ ] Add tests in spec/ directory that exercise cap commands against the Docker environment (e.g., cap deploy, cap execute)
- [ ] Validate SSH key authentication flow matches .docker/ssh_key_rsa setup
- [ ] Document in DEVELOPMENT.md how to run Docker-based tests locally
Add comprehensive upgrade guide from 2.x to 3.x in UPGRADING docs
The repo has UPGRADING-3.7.md covering upgrades to 3.7, but there's no UPGRADING-3.0.md or similar for the major 2.x → 3.x transition. Given the significant architectural changes in Capistrano v3, this is a high-friction point for users upgrading from legacy versions. This would reduce support burden and improve user experience.
- [ ] Create docs/UPGRADING-3.0.md documenting breaking changes from v2 to v3
- [ ] Include migration examples for Capfile structure, task definitions, and role configuration
- [ ] Add before/after code snippets for common patterns (e.g., using invoke vs. run in v2 → v3)
- [ ] Link UPGRADING-3.0.md from README.md and CONTRIBUTING.md
Add GitHub Action workflow for testing against multiple Ruby versions
The .github/workflows/ci.yml exists but isn't visible in the file structure provided. Capistrano likely needs to support multiple Ruby versions (2.7, 3.0, 3.1, 3.2+), but there's no explicit matrix testing visible. Adding a robust matrix build strategy would ensure compatibility and catch version-specific bugs early.
- [ ] Audit current .github/workflows/ci.yml for Ruby version matrix coverage
- [ ] Add/expand matrix strategy to test against Ruby 2.7, 3.0, 3.1, 3.2, 3.3 if not already present
- [ ] Ensure Gemfile specifies a ruby_version directive that matches tested versions
- [ ] Add allowed_failures for experimental Ruby versions (pre-release, JRuby, Truffleruby)
🌿Good first issues
- Add RSpec coverage for edge cases in lib/capistrano/ssh.rb connection pooling (visible from git blame if untested), such as SSH timeouts or stale connections on slow networks
- Document the exact Capfile syntax and stage variable precedence rules in docs/ (CNAME indicates Jekyll docs site)—search the issues for 'Capfile' + 'confusion' to find recurring questions
- Implement missing Gherkin scenarios in features/ for rollback failure recovery—look for @skip or @wip tags in .feature files indicating incomplete test cases
⭐Top contributors
Click to expand
Top contributors
- @mattbrictson — 57 commits
- @dependabot[bot] — 7 commits
- @leehambley — 5 commits
- @G-Rath — 4 commits
- @taketo1113 — 2 commits
📝Recent commits
Click to expand
Recent commits
c4a3950— Update cucumber requirement from ~> 10.1 to ~> 11.0 (#2184) (dependabot[bot])72fa723— Bump release-drafter/release-drafter from 6 to 7 (#2183) (dependabot[bot])dfe3133— Add Ruby 4 to CI (#2179) (mattbrictson)ac721d6— Update mocha requirement from ~> 2.8 to ~> 3.0 (#2178) (dependabot[bot])f9e932c— Release 3.20.0 (mattbrictson)7717f70— Bump release-drafter/release-drafter from 5 to 6 (#2174) (dependabot[bot])cb457ea— Bump actions/checkout from 4 to 6 (#2175) (dependabot[bot])747d5e4— Enable dependabot for Bundler and GitHub Actions (#2173) (mattbrictson)5533134— Upgrade to rubocop 1.81.7; generate.rubocop_todo.yml(#2172) (mattbrictson)2efe0d7— Drop support for Ruby 2.0, 2.1, 2.2, 2.3, and 2.4 (#2171) (mattbrictson)
🔒Security observations
- Critical · Hardcoded SSH Private Key in Repository —
.docker/ssh_key_rsa. The file.docker/ssh_key_rsacontains a private SSH key that is committed to the repository. This is a critical security vulnerability as anyone with access to the repository can use this key to authenticate as the user it was generated for. Private keys should never be stored in version control. Fix: Immediately revoke this SSH key. Remove it from the repository history using git filter-branch or BFG Repo-Cleaner. Generate a new key pair and store the private key securely outside version control (e.g., in CI/CD secrets, environment variables, or secure key management systems). Addssh_key_rsato.gitignoreto prevent accidental commits in the future. - High · SSH Server Exposed on Non-Standard Port Without Authentication Hardening —
docker-compose.yml, .docker/Dockerfile. The docker-compose.yml exposes an SSH server on port 2022. Combined with the hardcoded private key vulnerability, this creates a serious attack surface. The SSH service should be restricted to trusted networks and use strong authentication mechanisms. Fix: Restrict SSH access to specific IP ranges using firewall rules or network policies. Implement key rotation policies. Consider using certificate-based authentication instead of key pairs. Never expose SSH servers directly to untrusted networks. Document and enforce secure SSH configuration practices in deployment guidelines. - High · Potential Insecure SSH Configuration in Docker —
.docker/ubuntu_setup.sh. The.docker/ubuntu_setup.shscript likely configures the SSH server for testing purposes. Without reviewing the actual content, standard Docker SSH setups often disable security features like password authentication restrictions, enable root login, or use weak host key generation practices for convenience. Fix: Review the SSH configuration in the setup script and ensure: 1) Root login is disabled (PermitRootLogin no), 2) Password authentication is disabled (PasswordAuthentication no), 3) Strong host keys are generated, 4) Only necessary SSH features are enabled. Keep SSH daemon configuration hardened even in development/test environments. - Medium · Docker Image Built from Untrusted Base Without Pinned Version —
.docker/Dockerfile. The Dockerfile in.docker/Dockerfilelikely uses a base image without a specific version tag, which could result in building with an outdated or vulnerable base image. This makes the image non-reproducible and potentially vulnerable. Fix: Always use specific version tags for base images (e.g.,ubuntu:22.04instead ofubuntu:latest). Regularly update to newer versions and scan images with vulnerability scanners like Trivy or Grype. Consider using a minimal base image (alpine, distroless) to reduce attack surface. - Medium · Missing Dependency Vulnerability Scanning —
Gemfile, Gemfile.lock (if present). The Gemfile exists but no dependency lock file content was provided. Ruby gem dependencies could contain known vulnerabilities that are not currently being tracked or scanned in the CI/CD pipeline. Fix: Implement automated dependency vulnerability scanning in CI/CD using tools likebundler-audit,dependabot, orSnyk. Ensure Gemfile.lock is committed to enforce reproducible builds. Set up automated alerts for vulnerable dependencies and establish a process for timely patching. - Medium · Potential Code Injection via SSH Deployment —
Capistrano core functionality (lib/ directory not fully visible). Capistrano is a deployment automation tool that executes code on remote servers via SSH. If user input is not properly sanitized in deployment scripts, it could lead to command injection vulnerabilities, especially in Rake tasks or shell command execution. Fix: Review all Capistrano task definitions and ensure proper input validation and sanitization. Use parameterized commands instead of string interpolation for shell execution. Avoid using shell metacharacters in user-controlled input. Implement strict output encoding for any user-supplied data used in deploy scripts. - Low · Incomplete Security Documentation —
Repository root. While CONTRIBUTING.md and DEVELOPMENT.md exist, there is no explicit SECURITY.md file visible for security reporting guidelines or vulnerability disclosure policy. Fix: Create a SECURITY.md file following the format suggested by GitHub, including: 1) How to report security vulnerabilities responsibly, 2) Security contact information, 3) Supported versions and security update timeline, 4) Known
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.