CrackedRuby logo

CrackedRuby

Capistrano

Overview

Capistrano provides a Ruby DSL for defining deployment workflows that execute commands on remote servers. The framework operates through SSH connections, executing deployment tasks in a structured, repeatable manner across multiple servers simultaneously.

The core architecture centers around the Capistrano::Application class, which orchestrates task execution through Capistrano::Task objects. Tasks define discrete operations like file transfers, service restarts, or database migrations. The Capistrano::Configuration class manages server definitions, variables, and deployment parameters.

# config/deploy.rb
set :application, 'myapp'
set :repo_url, 'git@github.com:user/myapp.git'
set :deploy_to, '/var/www/myapp'

server 'web1.example.com', user: 'deploy', roles: %w{app web}
server 'web2.example.com', user: 'deploy', roles: %w{app web}
server 'db.example.com', user: 'deploy', roles: %w{db}

Server roles organize deployment targets by function. The app role typically handles application code, web manages web servers, and db controls database operations. Capistrano executes tasks against servers based on their assigned roles.

task :restart_services do
  on roles(:app) do
    execute 'sudo systemctl restart myapp'
  end
  on roles(:web) do
    execute 'sudo systemctl reload nginx'
  end
end

The deployment process follows a structured workflow: Capistrano creates timestamped release directories on target servers, deploys code to these directories, then atomically switches a current symlink to point to the new release. This approach enables instant rollbacks by pointing the symlink to a previous release directory.

Capistrano integrates with version control systems through SCM adapters. The Git adapter, most commonly used, clones repositories, checks out specific revisions, and transfers code to servers. Task dependencies ensure proper execution order, while hooks provide extension points for custom deployment logic.

Basic Usage

Capistrano deployment begins with generating configuration files through cap install. This command creates the deployment structure with environment-specific configurations and task definitions.

# Generate Capistrano configuration
# Command: cap install
# Creates:
# - Capfile
# - config/deploy.rb
# - config/deploy/production.rb
# - config/deploy/staging.rb
# - lib/capistrano/tasks/

The Capfile loads Capistrano components and custom tasks. Standard deployments require the base framework and SCM integration:

# Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/scm/git'

install_plugin Capistrano::SCM::Git

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

Basic deployment configuration defines application parameters, repository details, and server specifications. The :deploy_to path becomes the deployment root containing releases, shared resources, and the current symlink:

# config/deploy.rb
lock '3.17.0'

set :application, 'blog_app'
set :repo_url, 'git@github.com:company/blog_app.git'
set :branch, 'main'
set :deploy_to, '/opt/apps/blog_app'
set :keep_releases, 5

append :linked_files, 'config/database.yml', 'config/secrets.yml'
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/uploads'

Environment-specific configurations override global settings and define server inventories. Production environments typically specify multiple servers with distinct roles:

# config/deploy/production.rb
server 'app1.example.com', user: 'deploy', roles: %w{app web}, primary: true
server 'app2.example.com', user: 'deploy', roles: %w{app web}
server 'worker.example.com', user: 'deploy', roles: %w{app}
server 'db.example.com', user: 'deploy', roles: %w{db}

set :branch, 'production'
set :rails_env, 'production'

Task execution targets specific server roles through the on method. Tasks can execute shell commands, transfer files, or manipulate remote directories:

namespace :app do
  task :compile_assets do
    on roles(:web) do
      within release_path do
        execute 'npm', 'install'
        execute 'npm', 'run', 'build'
      end
    end
  end
  
  task :restart do
    on roles(:app) do
      execute 'sudo systemctl restart myapp'
    end
  end
end

Deployment execution follows the command pattern cap <environment> <task>. The standard deployment task orchestrates the complete workflow:

# Deploy to production
# cap production deploy

# Deploy specific branch
# cap production deploy BRANCH=feature-123

# Execute custom task
# cap production app:restart

Advanced Usage

Advanced Capistrano deployments leverage custom variables, complex task dependencies, and sophisticated server targeting. Variable scoping enables environment-specific behavior while maintaining configuration consistency across stages.

# Dynamic variable assignment
set :timestamp, -> { Time.now.strftime('%Y%m%d%H%M%S') }
set :backup_path, -> { "#{shared_path}/backups/#{fetch(:timestamp)}" }

# Conditional variables
set :worker_processes, proc {
  case fetch(:stage)
  when :production then 4
  when :staging then 2
  else 1
  end
}

# Server filtering by custom properties
server 'web1.example.com', user: 'deploy', roles: %w{web}, datacenter: 'us-east'
server 'web2.example.com', user: 'deploy', roles: %w{web}, datacenter: 'us-west'

task :deploy_by_datacenter do
  on roles(:web).select { |s| s.properties.datacenter == 'us-east' } do
    # Deploy only to east coast servers
  end
end

Complex task orchestration requires explicit dependency management and hook integration. Tasks can specify prerequisites, define execution order, and integrate with the deployment lifecycle:

namespace :database do
  task :backup => ['deploy:set_current_revision'] do
    on roles(:db) do
      backup_file = "backup_#{fetch(:current_revision)[0..7]}.sql"
      execute 'pg_dump', 'myapp_production', '>', "#{fetch(:backup_path)}/#{backup_file}"
    end
  end
  
  task :migrate => ['database:backup'] do
    on primary(:db) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute 'bundle exec rails db:migrate'
        end
      end
    end
  end
end

# Hook into deployment lifecycle
before 'deploy:updated', 'database:backup'
after 'deploy:updated', 'database:migrate'

Server communication supports advanced SSH configuration including jump hosts, custom authentication, and connection pooling. Complex network topologies require explicit SSH configuration:

# SSH jump host configuration
set :ssh_options, {
  user: 'deploy',
  keys: %w(/home/deploy/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey),
  proxy: Net::SSH::Proxy::Command.new('ssh gateway.example.com -W %h:%p')
}

# Custom SSH configuration per server
server 'internal.example.com', user: 'deploy', roles: %w{app}, ssh_options: {
  port: 2222,
  keys: %w(/home/deploy/.ssh/internal_rsa)
}

Multi-stage deployments with shared resources require careful coordination. Blue-green deployments minimize downtime through parallel environment management:

namespace :bluegreen do
  task :switch do
    on roles(:web) do
      current_color = test('[ -L /opt/apps/current ]') ? 
                     capture('readlink /opt/apps/current').split('/').last : 'blue'
      new_color = current_color == 'blue' ? 'green' : 'blue'
      
      # Deploy to inactive environment
      execute 'ln', '-sfn', "/opt/apps/#{new_color}", '/opt/apps/current'
      
      # Update load balancer
      execute 'curl', '-X POST', "http://lb.example.com/switch/#{new_color}"
    end
  end
  
  task :prepare do
    on roles(:app) do
      ['blue', 'green'].each do |color|
        execute 'mkdir', '-p', "/opt/apps/#{color}/shared/log"
        execute 'mkdir', '-p', "/opt/apps/#{color}/shared/tmp"
      end
    end
  end
end

Error Handling & Debugging

Deployment failures require systematic diagnosis through log analysis, connection testing, and environment validation. Capistrano provides debugging mechanisms for identifying failure points in complex deployment workflows.

Connection failures manifest through SSH authentication errors, network timeouts, or permission problems. Verbose output reveals connection attempts and failure reasons:

# Enable SSH debugging
set :ssh_options, {
  verbose: :debug,
  logger: Logger.new($stdout)
}

# Test connectivity before deployment
task :check_connectivity do
  on roles(:all) do |host|
    begin
      execute 'echo', 'Connection successful'
      info "Connected to #{host.hostname}"
    rescue SSHKit::Runner::ExecuteError => e
      error "Connection failed to #{host.hostname}: #{e.message}"
      raise
    end
  end
end

Command execution errors require analysis of exit codes, output streams, and environmental factors. Failed commands should provide diagnostic information:

task :safe_restart do
  on roles(:app) do
    begin
      # Test service status before restart
      service_status = capture 'systemctl is-active myapp || echo inactive'
      
      if service_status.strip == 'inactive'
        warn 'Service already stopped, starting instead of restarting'
        execute 'sudo systemctl start myapp'
      else
        execute 'sudo systemctl restart myapp'
      end
      
      # Verify restart success
      sleep 2
      execute 'systemctl is-active myapp'
      
    rescue SSHKit::Command::Failed => e
      error "Service restart failed: #{e.message}"
      
      # Gather diagnostic information
      execute 'sudo journalctl -u myapp --no-pager -n 20'
      execute 'sudo systemctl status myapp'
      
      raise 'Deployment failed: unable to restart application service'
    end
  end
end

File transfer failures occur due to permission issues, disk space constraints, or network interruptions. Robust deployments validate preconditions and handle partial transfers:

namespace :deploy do
  task :check_disk_space do
    on roles(:app) do
      available_mb = capture("df #{fetch(:deploy_to)} | tail -1 | awk '{print $4}'").to_i / 1024
      required_mb = 500  # Minimum required MB
      
      if available_mb < required_mb
        error "Insufficient disk space: #{available_mb}MB available, #{required_mb}MB required"
        raise 'Deployment aborted due to insufficient disk space'
      end
      
      info "Disk space check passed: #{available_mb}MB available"
    end
  end
  
  task :verify_upload do
    on roles(:app) do
      # Verify critical files exist after upload
      %w[Gemfile config.ru].each do |file|
        unless test("[ -f #{release_path}/#{file} ]")
          error "Critical file missing after upload: #{file}"
          raise "Incomplete upload detected"
        end
      end
    end
  end
end

before 'deploy:updating', 'deploy:check_disk_space'
after 'deploy:updated', 'deploy:verify_upload'

Task execution debugging requires understanding execution context, variable scope, and timing issues. Debug output reveals execution flow:

task :debug_environment do
  on roles(:app) do |host|
    info "=== Debug Information for #{host.hostname} ==="
    info "Deploy path: #{fetch(:deploy_to)}"
    info "Current path: #{current_path}"
    info "Release path: #{release_path}"
    info "Shared path: #{shared_path}"
    
    # Display environment variables
    execute 'env | grep -E "(RAILS_ENV|NODE_ENV|PATH)" | sort'
    
    # Check Ruby/Rails versions
    within release_path do
      execute 'bundle --version'
      execute 'ruby --version'
    end
    
    # Verify symlinks
    execute 'ls -la', current_path if test("[ -L #{current_path} ]")
  end
end

Rollback scenarios require state validation and cleanup procedures. Failed deployments should restore previous stable states:

namespace :deploy do
  task :rollback_with_verification do
    on roles(:app) do
      # Store current release for recovery
      failed_release = release_path
      
      # Execute standard rollback
      invoke 'deploy:rollback'
      
      # Verify rollback success
      begin
        within current_path do
          execute 'bundle exec rails runner "puts Rails.env"'
        end
        info 'Rollback verification successful'
        
        # Cleanup failed release
        execute 'rm', '-rf', failed_release
        
      rescue SSHKit::Command::Failed
        error 'Rollback verification failed'
        # Manual intervention required
        raise 'Rollback completed but application failed to start'
      end
    end
  end
end

Production Patterns

Production Capistrano deployments require zero-downtime strategies, health monitoring, and coordination with external systems. Deployment workflows integrate with load balancers, monitoring systems, and notification services.

Zero-downtime deployments coordinate application restarts with load balancer management. Servers exit rotation during updates, preventing request interruption:

namespace :loadbalancer do
  task :remove_from_rotation do
    on roles(:web) do |host|
      # Remove server from load balancer
      execute 'curl', '-X DELETE', 
              "http://lb-api.example.com/pool/production/#{host.hostname}"
      
      # Wait for active connections to drain
      sleep 30
      
      # Verify no active connections
      active_connections = capture('netstat -an | grep :80 | grep ESTABLISHED | wc -l').to_i
      if active_connections > 5
        warn "#{active_connections} connections still active on #{host.hostname}"
      end
    end
  end
  
  task :add_to_rotation do
    on roles(:web) do |host|
      # Add server back to load balancer
      execute 'curl', '-X POST', 
              "http://lb-api.example.com/pool/production/#{host.hostname}"
      
      # Verify health check passes
      5.times do |attempt|
        response = capture("curl -s -o /dev/null -w '%{http_code}' http://#{host.hostname}/health")
        
        if response == '200'
          info "Health check passed for #{host.hostname}"
          break
        elsif attempt == 4
          error "Health check failed after 5 attempts"
          raise "Server #{host.hostname} failing health checks"
        else
          sleep 10
        end
      end
    end
  end
end

before 'deploy:restart', 'loadbalancer:remove_from_rotation'
after 'deploy:restart', 'loadbalancer:add_to_rotation'

Database migration coordination prevents schema conflicts in multi-server environments. Migration tasks execute on single servers with proper locking:

namespace :database do
  task :migrate_with_lock do
    on primary(:db) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          # Acquire migration lock
          lock_file = "#{shared_path}/db_migrate.lock"
          
          if test("[ -f #{lock_file} ]")
            lock_age = capture("stat -c %Y #{lock_file}").to_i
            current_time = capture('date +%s').to_i
            
            # Remove stale locks (older than 30 minutes)
            if current_time - lock_age > 1800
              execute 'rm', lock_file
              warn 'Removed stale migration lock'
            else
              raise 'Migration already in progress'
            end
          end
          
          begin
            # Create lock file
            execute 'touch', lock_file
            
            # Run migrations
            execute 'bundle exec rails db:migrate'
            
            # Verify migration status
            migration_status = capture('bundle exec rails db:migrate:status | grep "  down  " | wc -l').to_i
            if migration_status > 0
              error "#{migration_status} migrations failed to apply"
              raise 'Migration verification failed'
            end
            
          ensure
            # Always remove lock
            execute 'rm', '-f', lock_file
          end
        end
      end
    end
  end
end

Asset compilation and CDN integration require coordination between deployment and content delivery. Compiled assets upload to CDN endpoints before application restart:

namespace :assets do
  task :compile_and_upload do
    on roles(:web), in: :sequence do
      within release_path do
        with rails_env: fetch(:rails_env) do
          # Compile assets with production optimizations
          execute 'bundle exec rails assets:precompile'
          
          # Generate asset manifest
          asset_manifest = "#{release_path}/public/assets/.sprockets-manifest-*.json"
          manifest_content = capture("cat #{asset_manifest}")
          
          # Upload to CDN
          execute 'aws s3 sync', 
                  "#{release_path}/public/assets/",
                  's3://cdn.example.com/assets/',
                  '--delete --cache-control max-age=31536000'
          
          # Update CDN cache headers
          execute 'aws cloudfront create-invalidation',
                  '--distribution-id E123456789',
                  '--paths "/assets/*"'
        end
      end
    end
  end
  
  task :verify_cdn_sync do
    on roles(:web), limit: 1 do
      # Test asset availability from CDN
      test_asset = capture("ls #{release_path}/public/assets/application-*.js | head -1")
      asset_name = File.basename(test_asset)
      
      response = capture("curl -s -o /dev/null -w '%{http_code}' https://cdn.example.com/assets/#{asset_name}")
      
      unless response == '200'
        error 'CDN asset sync verification failed'
        raise 'Assets not available from CDN'
      end
      
      info 'CDN asset sync verified'
    end
  end
end

Monitoring integration provides deployment visibility and failure notification. Deployment events integrate with APM and alerting systems:

namespace :monitoring do
  task :notify_deployment_start do
    run_locally do
      # Notify monitoring system
      execute 'curl', '-X POST',
              'https://api.datadog.com/api/v1/events',
              '-H "Content-Type: application/json"',
              '-H "DD-API-KEY: $DATADOG_API_KEY"',
              '-d', %Q{
                {
                  "title": "Deployment Started",
                  "text": "#{fetch(:application)} deployment to #{fetch(:stage)} started",
                  "tags": ["deployment", "#{fetch(:stage)}", "#{fetch(:application)}"]
                }
              }
    end
  end
  
  task :health_check_monitoring do
    on roles(:web) do |host|
      # Register deployment event
      execute 'curl', '-X POST',
              "http://monitoring.example.com/events",
              '-d', "service=#{fetch(:application)}&host=#{host.hostname}&event=deployment_complete"
      
      # Enable enhanced monitoring during deployment window
      execute 'curl', '-X POST',
              "http://monitoring.example.com/alerts/#{fetch(:application)}/enhanced",
              '-d', 'duration=300'  # 5 minutes enhanced monitoring
    end
  end
end

before 'deploy:started', 'monitoring:notify_deployment_start'
after 'deploy:finished', 'monitoring:health_check_monitoring'

Common Pitfalls

Capistrano deployments fail through subtle configuration issues, timing problems, and environmental inconsistencies. Understanding common failure patterns prevents deployment disruptions and reduces troubleshooting time.

SSH key authentication problems cause deployment failures when key forwarding fails or authentication agents lack required keys. Agent forwarding requires proper SSH configuration:

# Incorrect - agent forwarding disabled
set :ssh_options, {
  user: 'deploy',
  keys: %w(/home/deploy/.ssh/id_rsa)
}

# Correct - enable agent forwarding for Git operations
set :ssh_options, {
  user: 'deploy',
  keys: %w(/home/deploy/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}

# Test SSH agent forwarding
task :test_ssh_agent do
  on roles(:app) do
    # This should succeed with agent forwarding
    execute 'ssh-add -l'
    
    # Test Git access through forwarded agent
    execute 'git ls-remote', fetch(:repo_url), 'HEAD'
  end
end

File permission errors occur when deploy users lack ownership or write access to deployment directories. Permission issues manifest during file transfers or symlink creation:

# Setup proper permissions during server provisioning
namespace :provision do
  task :setup_deploy_user do
    on roles(:all) do
      # Create deployment directory with proper ownership
      execute 'sudo mkdir -p', fetch(:deploy_to)
      execute 'sudo chown', "#{fetch(:user)}:#{fetch(:user)}", fetch(:deploy_to)
      execute 'chmod', '755', fetch(:deploy_to)
      
      # Setup shared directory structure
      shared_dirs = fetch(:linked_dirs) + ['log', 'tmp', 'config']
      shared_dirs.each do |dir|
        execute 'mkdir', '-p', "#{shared_path}/#{dir}"
      end
    end
  end
  
  # Verify permissions before deployment
  task :check_permissions do
    on roles(:app) do
      # Test write access to deployment directory
      test_file = "#{fetch(:deploy_to)}/.deploy_test"
      execute 'touch', test_file
      execute 'rm', test_file
      
      # Verify shared directory permissions
      fetch(:linked_dirs).each do |dir|
        unless test("[ -w #{shared_path}/#{dir} ]")
          error "No write permission for #{shared_path}/#{dir}"
          raise 'Permission check failed'
        end
      end
    end
  end
end

Symlink timing issues create race conditions when applications start before symlinks complete. Atomic symlink operations prevent partial deployment states:

# Problematic - non-atomic symlink creation
task :unsafe_symlink do
  on roles(:app) do
    execute 'rm', current_path
    execute 'ln -s', release_path, current_path
    # Race condition: application may start between rm and ln
  end
end

# Safe - atomic symlink replacement
task :atomic_symlink do
  on roles(:app) do
    temp_link = "#{current_path}.tmp.#{Time.now.to_i}"
    execute 'ln -s', release_path, temp_link
    execute 'mv', temp_link, current_path
    # Atomic operation: symlink replacement happens instantly
  end
end

Environment variable inheritance problems cause runtime failures when deployed applications expect variables unavailable in Capistrano's execution context:

# Missing environment variables cause subtle failures
task :problematic_env do
  on roles(:app) do
    # This may fail if RAILS_ENV not properly set
    within current_path do
      execute 'bundle exec rails runner "puts Rails.env"'
    end
  end
end

# Explicit environment variable management
task :safe_env do
  on roles(:app) do
    within current_path do
      # Load environment from multiple sources
      env_vars = {
        'RAILS_ENV' => fetch(:rails_env),
        'NODE_ENV' => 'production'
      }
      
      # Source environment files if they exist
      if test("[ -f #{shared_path}/.env ]")
        env_vars.merge!(
          Hash[capture("cat #{shared_path}/.env").split("\n").map { |line| line.split('=', 2) }]
        )
      end
      
      with env_vars do
        execute 'bundle exec rails runner "puts Rails.env"'
      end
    end
  end
end

Deployment cleanup failures leave systems in inconsistent states. Failed deployments require explicit cleanup procedures:

namespace :deploy do
  # Dangerous - leaves failed releases
  task :deploy_without_cleanup do
    invoke 'deploy:updating'
    invoke 'deploy:updated'
    invoke 'deploy:publishing'  # This might fail
    invoke 'deploy:published'
  end
  
  # Safe - cleanup on failure
  task :deploy_with_cleanup do
    begin
      invoke 'deploy:updating'
      invoke 'deploy:updated'
      invoke 'deploy:publishing'
      invoke 'deploy:published'
      
    rescue => e
      error "Deployment failed: #{e.message}"
      
      # Cleanup failed release
      on roles(:app) do
        if test("[ -d #{release_path} ]")
          execute 'rm', '-rf', release_path
          info "Cleaned up failed release: #{release_path}"
        end
        
        # Verify current symlink still points to working release
        if test("[ -L #{current_path} ]")
          target = capture("readlink #{current_path}")
          unless test("[ -d #{target} ]")
            error "Current symlink points to missing directory: #{target}"
            # Emergency rollback needed
          end
        end
      end
      
      raise
    end
  end
end

Concurrent deployment conflicts occur when multiple deployment processes target the same servers simultaneously. Deployment locking prevents conflicts:

namespace :deploy do
  task :with_lock do
    on roles(:app), limit: 1 do
      lock_file = "#{fetch(:deploy_to)}/.deploy.lock"
      lock_timeout = 1800  # 30 minutes
      
      # Check for existing lock
      if test("[ -f #{lock_file} ]")
        lock_pid = capture("cat #{lock_file}").strip
        lock_time = capture("stat -c %Y #{lock_file}").to_i
        current_time = Time.now.to_i
        
        if current_time - lock_time > lock_timeout
          warn "Removing stale deployment lock (PID: #{lock_pid})"
          execute 'rm', lock_file
        else
          raise "Deployment already in progress (PID: #{lock_pid})"
        end
      end
      
      begin
        # Create lock file with current process info
        execute 'echo', "$$_#{Time.now.to_i}", '>', lock_file
        
        # Proceed with deployment
        yield
        
      ensure
        # Always remove lock
        execute 'rm', '-f', lock_file
      end
    end
  end
end

# Wrap standard deployment with locking
task :deploy do
  invoke 'deploy:with_lock' do
    invoke 'deploy:starting'
    invoke 'deploy:started'
    invoke 'deploy:updating'
    invoke 'deploy:updated'
    invoke 'deploy:publishing'
    invoke 'deploy:published'
    invoke 'deploy:finishing'
    invoke 'deploy:finished'
  end
end

Reference

Core Configuration Variables

Variable Type Default Description
:application String nil Application name for deployment identification
:repo_url String nil Source control repository URL
:branch String 'main' Git branch or tag for deployment
:deploy_to String nil Remote deployment root directory
:scm Symbol :git Source control management system
:format Symbol :airbrussh Log output formatting style
:log_level Symbol :debug Logging verbosity level
:pty Boolean false Allocate pseudo-terminal for commands
:keep_releases Integer 5 Number of releases to retain

Path Variables

Variable Type Description
current_path Pathname Symlink to current release directory
release_path Pathname Current deployment release directory
releases_path Pathname Directory containing all releases
shared_path Pathname Directory for shared resources
repo_path Pathname Local repository cache directory

Server Configuration

# Server definition with roles and properties
server 'hostname', user: 'deploy', roles: %w{role1 role2}, properties: {}

# SSH options per server
server 'hostname', ssh_options: {
  port: 2222,
  keys: %w(/path/to/key),
  forward_agent: true,
  user: 'deploy'
}

Task Definition Methods

Method Parameters Description
task(name, &block) name (Symbol), block (Proc) Define deployment task
namespace(name, &block) name (Symbol), block (Proc) Group related tasks
desc(description) description (String) Document task purpose
before(task, hook) task (Symbol), hook (Symbol) Execute hook before task
after(task, hook) task (Symbol), hook (Symbol) Execute hook after task

Execution Context Methods

Method Parameters Returns Description
on(servers, &block) servers (Array), block (Proc) void Execute block on specified servers
roles(names) names (Array/Symbol) Array<Server> Get servers matching role names
primary(role) role (Symbol) Server Get primary server for role
execute(command, *args) command (String), args (Array) void Execute command on remote server
capture(command, *args) command (String), args (Array) String Capture command output
test(command, *args) command (String), args (Array) Boolean Test command exit status
within(path, &block) path (String), block (Proc) void Execute block within directory
with(env, &block) env (Hash), block (Proc) void Execute block with environment variables

Built-in Tasks

Task Description
deploy Complete deployment workflow
deploy:check Verify deployment configuration
deploy:rollback Rollback to previous release
deploy:restart Restart application services
deploy:cleanup Remove old releases
deploy:cleanup_rollback Remove rollback release

SCM Configuration

# Git-specific configuration
set :repo_url, 'git@github.com:user/repo.git'
set :branch, 'main'
set :git_shallow_clone, 1

# Subversion configuration  
set :scm, :subversion
set :repo_url, 'https://svn.example.com/repo/trunk'

Error Classes

Class Description
SSHKit::Runner::ExecuteError Command execution failure
SSHKit::Command::Failed Command returned non-zero exit
Net::SSH::AuthenticationFailed SSH authentication failure
Capistrano::ValidationError Configuration validation error