Skip to content

Capistrano vs Kamal: Comprehensive Comparison #23

@hungle00

Description

@hungle00

Table of Contents

Overview

The main differences between Capistrano and Kamal are:

  • Capistrano is a traditional server-based deployment tool that uses SSH and file system deployment.
  • Kamal is a container-based deployment tool that uses Docker and container orchestration.

Capistrano Architecture

Local Machine (Client) ←→ Remote Server (Target)
     ↓
Capistrano Tasks
     ↓
SSH Connection
     ↓
Remote Commands Execution
     ↓
File System Deployment

Key Components:

  • Capfile (entry point)
  • config/deploy.rb (main configuration)
  • config/deploy/environment.rb (environment-specific)
  • SSH key authentication
  • Server file system management

Kamal Architecture

Local Machine ←→ Container Registry ←→ VPS Server
     ↓              ↓                    ↓
Kamal CLI    ←→ Docker Images    ←→ Docker Containers
     ↓              ↓                    ↓
Deploy Commands ←→ Image Build   ←→ Application Running

Key Components:

  • config/deploy.yml (single configuration file)
  • Dockerfile (container definition)
  • Container registry (Docker Hub, GitHub Container Registry)
  • Traefik (reverse proxy and load balancer)

Setup Process

Capistrano Setup

1. Add Gems to Gemfile

# Gemfile
group :development do
  gem 'capistrano', '~> 3.17'
  gem 'capistrano-rails', '~> 1.6'
  gem 'capistrano-rbenv', '~> 2.2'
  gem 'capistrano3-puma', '~> 5.2'
  gem 'capistrano-postgresql', '~> 7.0'
end

2. Install Capistrano

bundle install
bundle exec cap install

This command will create all the necessary configuration files and directory structure

├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
└── lib
    └── capistrano
            └── tasks

3. Server Preparation

# On VPS server
sudo adduser deploy
sudo usermod -aG sudo deploy
sudo mkdir -p /var/www/meditation
sudo chown deploy:deploy /var/www/meditation

# Install required software
sudo apt update
sudo apt install nginx postgresql redis-server

Kamal Setup

1. Add Kamal Gem

# Gemfile
gem "kamal", require: false

2. Initialize Kamal

bundle install
kamal init

This command will create config/deploy.yml - a configuration yml file for container deployment.

3. Create Dockerfile

Otherwise, you need to create Dockerfile to build image for this project.

Workflow Comparison

Capistrano Workflow

Deployment Process

# 1. Check deployment prerequisites
cap production deploy:check

# 2. Deploy application
cap production deploy

Internal Steps:

  1. deploy:starting - Initialize deployment
  2. deploy:updating - Update code from Git
  3. deploy:reverting - Rollback if needed
  4. deploy:publishing - Publish application
  5. deploy:finishing - Complete deployment

File System Structure

/var/www/meditation/
├── current/          # Symlink to latest release
├── releases/         # All deployed versions
│   ├── 20231201120000/
│   ├── 20231201130000/
│   └── 20231201140000/
├── shared/           # Shared files between releases
│   ├── log/
│   ├── tmp/
│   ├── public/
│   └── config/
└── repo/             # Git repository

Kamal Workflow

Deployment Process

# 1. Build and push Docker image
docker build -t your-registry/meditation:latest .
docker push your-registry/meditation:latest

# 2. Deploy application
kamal deploy

Internal Steps:

  1. Image Pull - Pull latest image from registry
  2. Container Stop - Stop current container
  3. Container Start - Start new container
  4. Health Check - Verify application health
  5. Traefik Update - Update routing configuration

Container Structure

Docker Containers:
├── meditation-app    # Rails application
├── postgres          # Database
├── redis             # Cache/Queue
└── traefik           # Reverse proxy

Configuration Examples

Kamal is easier to configure compared with Capistrano. With Kamal, you need a Dockerfile and one YAML file to define services. With Capistrano, you need to install and configure for each service on the server, for example, puma, nginx, ...

Capistrano Configuration

This is config from my old project, use for deploying on Linode VPS
Capfile

# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"
require "capistrano/secrets_yml"
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# Include tasks from other gems included in your Gemfile
# require "capistrano/rvm"
require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require 'capistrano/puma'
install_plugin Capistrano::Puma  # Default puma tasks
install_plugin Capistrano::Puma::Systemd
# require "capistrano/passenger"

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

config/deploy.rb

# config valid for current version and patch releases of Capistrano
lock "~> 3.17.2"

set :application, "hotwire_way"
set :repo_url, "git@github.com:hungle00/hotwire-way.git"

set :rbenv_type, :user
set :rbenv_ruby, '3.0.0'

# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
set :branch, :develop

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/home/ubuntu/#{fetch :application}"
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.error.log"
set :puma_error_log,  "#{release_path}/log/puma.access.log"
set :puma_systemctl_user, :system
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true  # Change to false when not using ActiveRecord

# Default value for :format is :airbrussh.
# set :format, :airbrussh

# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
# append :linked_files, "config/database.yml", 'config/master.key'
append :linked_files,  'config/credentials/production.key'

# Default value for linked_dirs is []
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "tmp/webpacker", "public/system", "vendor", "storage"

Environment Configuration

# config/deploy/production.rb
server 'your-vps-ip', user: 'deploy', roles: %w{app db web}
set :branch, 'main'
set :rails_env, 'production'
set :default_env, {
  'RAILS_ENV' => 'production',
  'DATABASE_URL' => 'postgresql://user:pass@localhost/meditation_production'
}

Configuration for puma and nginx

See here
https://github.com/hungle00/hotwire-way/blob/develop/puma_nginx.md

Kamal Configuration

Main Configuration

# config/deploy.yml
service: meditation
image: your-registry/meditation
registry:
  username: your-username
  password:
    - KAMAL_REGISTRY_PASSWORD

servers:
  web:
    hosts:
      - your-vps-ip
    labels:
      traefik.http.routers.meditation.rule: Host(`your-domain.com`)
      traefik.http.routers.meditation.tls: "true"
    env:
      RAILS_ENV: production
      DATABASE_URL: postgresql://user:pass@host/db
      REDIS_URL: redis://host:6379/1
    volumes:
      - "/var/lib/meditation/storage:/app/storage"
      - "/var/lib/meditation/logs:/app/log"

accessories:
  db:
    image: postgres:15
    host: your-vps-ip
    port: 5432
    env:
      POSTGRES_DB: meditation_production
      POSTGRES_USER: meditation
      POSTGRES_PASSWORD: your-password
    volumes:
      - "/var/lib/meditation/postgres:/var/lib/postgresql/data"

  redis:
    image: redis:7-alpine
    host: your-vps-ip
    port: 6379
    volumes:
      - "/var/lib/meditation/redis:/data"

Deployment Process

Capistrano Deployment

Pre-deployment Checks

# Check deployment prerequisites
cap production deploy:check

Deployment Commands

# Full deployment
cap production deploy

# Deploy with specific branch
cap production deploy BRANCH=feature/new-feature

# Deploy with custom environment
cap production deploy RAILS_ENV=staging

Deployment Steps

  1. Bundle Install

    bundle install --path vendor/bundle --without development test
  2. Database Migration

    RAILS_ENV=production bundle exec rake db:migrate
  3. Asset Precompilation

    RAILS_ENV=production bundle exec rake assets:precompile
  4. Symlink Update

    rm /var/www/meditation/current
    ln -s /var/www/meditation/releases/20231201140000 /var/www/meditation/current
  5. Application Restart

    sudo systemctl restart puma_meditation

Kamal Deployment

Pre-deployment Checks

# Check configuration
kamal config

# Validate deployment
kamal deploy --dry-run

Deployment Commands

# Build and deploy
kamal deploy

# Deploy specific version
kamal deploy v1.2.3

# Deploy with custom environment
kamal deploy --env production

Deployment Steps

  1. Image Build

    docker build -t your-registry/meditation:latest .
  2. Image Push

    docker push your-registry/meditation:latest
  3. Container Deployment

    # Pull new image
    docker pull your-registry/meditation:latest
    
    # Stop old container
    docker stop meditation-app
    
    # Start new container
    docker run -d --name meditation-app-new your-registry/meditation:latest
  4. Traefik Update

    # Update routing to new container
    docker exec traefik traefik config

Monitoring & Logging

Capistrano Monitoring

Log Management

# Application logs
tail -f /var/www/meditation/current/log/production.log

# Puma logs
sudo journalctl -u puma_meditation -f

# Nginx logs
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

Health Checks

# Check application status
curl -f http://your-domain.com/health

# Check Puma status
sudo systemctl status puma_meditation

# Check database connection
RAILS_ENV=production bundle exec rails console

Kamal Monitoring

Container Logs

# View all logs
kamal logs

# View specific service logs
kamal logs web

# Follow logs in real-time
kamal logs -f

# View container logs directly
docker logs meditation-app

Health Monitoring

# Check service status
kamal status

# Check container health
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# Check resource usage
kamal exec web top

Conclusion

Kamal represents the future of Rails deployment with its modern approach. The main pros of Kamal over Capistrano are:

  • Modern Container-based: Kamal uses Docker instead of direct server management, making it easier to scale and maintain.
  • Automatic Zero-downtime: Kamal has built-in zero-downtime deployment, while Capistrano requires complex manual configuration.
  • Quick Setup: Kamal is easier to learn and requires less time to set up.
  • Rails-focused Design: Kamal is specifically designed for Rails with built-in monitoring, health checks, and secrets management.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cloudNote about AWS, Azure, Heroku, ...rubyAbout Ruby and Rails

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions