Deploying Rails 8 with Kamal 2: A Complete Guide

by | Sep 1, 2025 | Coding, DevOps & Deployment Series | 0 comments

Reading Time: ~8 minutes | DevOps & Deployment Series

Ember’s Opening Wisdom:
“A penguin’s nest may seem humble, but it’s built to last through every storm. Kamal 2 turns Rails deployment from a tangled iceberg into smooth open water. Let’s turn DevOps into a sanctuary, not a scramble.” 🐧🔥


Table of Contents

  1. Why Kamal 2?
  2. Preparing Your Rails 8 App for Kamal
  3. Infrastructure Choices: Bare Metal, Cloud, or Hybrid?
  4. Dockerizing Rails 8: The Foundation
  5. Introducing Kamal 2: Philosophy & Overview
  6. Step-by-Step: Rails 8 + Kamal 2 Deployment
  7. Zero-Downtime Deploys: What’s Really Happening?
  8. Advanced Kamal Features: Secrets, Rollbacks, Hooks
  9. CI/CD Integration: GitHub Actions & Kamal
  10. Real-World Gotchas & Troubleshooting
  11. Security, Compliance, and Observability
  12. Faith-Tech Perspective: DevOps as Ministry
  13. Checklist & Resources
  14. Closing Reflections

1. Why Kamal 2? The Rails Deployment Revolution

The Rails world has changed. Gone are the days of Heroku’s magic (and mounting bills), Capistrano’s SSH gymnastics, or hand-rolled Docker Compose scripts. When Rails 8 shipped in late 2024, it brought with it a new expectation: modern apps deserve modern, repeatable, zero-downtime deployments.

Enter Kamal 2.
Kamal (formerly MRSK) is the Rails core team’s answer to container-native deployment—simple, open, cloud-agnostic, and fast. It’s built for Rails 8+, but flexible enough for earlier versions.

Why use Kamal 2?

  • True zero-downtime deploys, even for large teams or apps with background jobs
  • Native Docker support: dev, staging, and prod are nearly identical
  • Infrastructure agnostic: DigitalOcean, Hetzner, AWS, Linode, bare metal, or hybrid
  • Open source, with Rails-style conventions and documentation
  • Built-in hooks for migrations, asset precompilation, cache purges, and more

For faith-based teams, Kamal’s philosophy fits perfectly: minimize risk, maximize stewardship, and keep your focus on serving people, not fighting servers.


2. Preparing Your Rails 8 App for Kamal

Before Kamal, you need a healthy Rails 8 app. Here’s the battle-tested approach from Topher.codes:

A. Upgrade Rails & Dependencies

  • Ensure you’re on Rails 8.0.1+ and Ruby 3.4.1+ (see [install guide for Apple Silicon])
  • Run bundle update and resolve all gem conflicts (especially for pg, sidekiq, and asset gems)
  • Test in production mode locally: RAILS_ENV=production rails s

B. Audit Your Gemfile

  • Remove deprecated gems (e.g., Webpacker, Puma if not using)
  • Add solid_queue or keep sidekiq for jobs
  • Add jsbundling-rails and/or cssbundling-rails for asset pipeline

C. Ready Your App for Docker

  • Use standard Rails directory structure
  • Avoid hardcoding environment-specific values (use ENV vars)
  • Ensure config/database.yml has production, staging, and test sections with env variables

3. Infrastructure Choices: Bare Metal, Cloud, or Hybrid?

Kamal 2 is infrastructure-agnostic, but your choices matter.

Common Setups:

  • DigitalOcean/Hetzner: Affordable, reliable, great for non-profits
  • AWS/Azure: Enterprise features, more complexity
  • Bare Metal: Max control, best for high privacy/ministry compliance
  • Hybrid: Mix of cloud for web, bare metal for database

Checklist:

  • SSH access to all nodes
  • Docker (24.0+) installed
  • Sufficient RAM (2GB+ per Rails instance)
  • Firewall rules set (allow HTTP/HTTPS from world, SSH from your IP)

4. Dockerizing Rails 8: The Foundation

Why Docker?
Consistency, repeatability, and no “works on my machine” drama.

Sample Dockerfile for Rails 8:

FROM ruby:3.4.1

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

WORKDIR /app
COPY . /app

RUN bundle install
RUN yarn install --check-files || true

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh

ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]

Tips:

  • Use multi-stage builds for large assets
  • Don’t copy dev/test files into production image
  • Store secrets outside the image (Kamal handles this)

5. Introducing Kamal 2: Philosophy & Overview

Kamal means “perfection” or “lotus” in several languages—a nod to simplicity and resilience. For Rails devs, it means:

  • One CLI (kamal): deploy, rollback, scale, logs, SSH, all in one place
  • One Config File (kamal.yml): describes your app, servers, secrets, and hooks
  • Zero Vendor Lock-In: move between clouds, or even run locally

Ember’s takeaway: “Kamal is the spiritual successor to Capistrano, minus the legacy pain.”


6. Step-by-Step: Rails 8 + Kamal 2 Deployment

A. Install Kamal

gem install kamal

Or add to your Gemfile:

group :development do
  gem 'kamal'
end

B. kamal.yml: Your Deployment Manifest

Example config (config/deploy/kamal.yml):

service: prayer-nook
image: ghcr.io/topher/prayer-nook
servers:
  - 203.0.113.10 # Web 1
  - 203.0.113.11 # Web 2
env:
  RAILS_ENV: production
  DATABASE_URL: <%= ENV["DATABASE_URL"] %>
  SECRET_KEY_BASE: <%= ENV["SECRET_KEY_BASE"] %>
registry:
  server: ghcr.io
  username: <%= ENV["GITHUB_ACTOR"] %>
  password: <%= ENV["GITHUB_TOKEN"] %>
hooks:
  setup:
    - bin/rails db:setup
  deploy:
    - bin/rails db:migrate
    - bin/rails assets:precompile
    - bin/rails solid_queue:restart
    - bin/rails cloudflare:purge

Pro tip: Store secrets in .env files or use Kamal’s encrypted secrets support.

C. Build and Push Docker Image

kamal build
kamal push

D. First Deploy

kamal setup
kamal deploy

What happens under the hood:

  • Kamal SSHs into each server, pulls your image from the registry
  • Runs pre/post-deploy hooks (migrations, assets)
  • Swaps running containers with zero downtime
  • Notifies you of result and logs output

7. Zero-Downtime Deploys: What’s Really Happening?

Kamal orchestrates a rolling update:

  • New container version is started alongside the old one
  • Health checks ensure the new container is live
  • Traffic is switched to the new container (via load balancer or Kamal’s built-in proxy)
  • Old container is stopped and removed

Bonus:
If a deploy fails, Kamal can instantly roll back to the previous container. No more “I hope we have a backup tarball somewhere.”


8. Advanced Kamal Features: Secrets, Rollbacks, Hooks

A. Managing Secrets

  • Use .env files for each environment (keep out of version control)
  • Kamal can encrypt/decrypt secrets for team sharing
  • Supports Docker secrets API for runtime injection

B. Rollbacks

kamal rollback
  • Instantly reverts to previous container image
  • Preserves database (unless migration broke it—have backups!)

C. Deployment Hooks

  • setup: Run once per server (db:setup, storage, etc.)
  • deploy: Run on each deploy (migrations, assets, cache purge)
  • healthcheck: Custom health endpoint to wait for before swapping

9. CI/CD Integration: GitHub Actions & Kamal

Sample Workflow (.github/workflows/deploy.yml):

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: ruby/setup-ruby@v1
      with:
        ruby-version: 3.4.1
    - name: Build Docker Image
      run: docker build -t ghcr.io/topher/prayer-nook:${{ github.sha }} .
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    - name: Push Docker Image
      run: docker push ghcr.io/topher/prayer-nook:${{ github.sha }}
    - name: Kamal Deploy
      env:
        KAMAL_ENV: production
        DATABASE_URL: ${{ secrets.DATABASE_URL }}
        SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
      run: |
        gem install kamal
        kamal deploy

Notes:

  • Use secrets for all sensitive credentials
  • Send deploy notifications via Slack/email for visibility

10. Real-World Gotchas & Troubleshooting

A. Asset Pipeline Surprises

  • Precompile assets within your container image, or use a dedicated assets job
  • If assets are missing in production: check public/assets is present, and RAILS_SERVE_STATIC_FILES is set

B. Database Migration Race

  • In multi-node setups, run migrations on a single node only (primary: true in Kamal config)
  • Use advisory locks for long-running migrations

C. Background Jobs

  • For Sidekiq: run as a separate service in Kamal
  • For Solid Queue: built-in to Rails 8, just restart queue with each deploy

D. Healthchecks

  • Implement a /healthz endpoint in Rails that checks DB, cache, and job queue status
  • Configure Kamal to use this for post-deploy validation

E. Log Management

  • Aggregate logs with a centralized tool (Logtail, Papertrail, or self-hosted ELK)
  • Mount /var/log/app or use Docker logging drivers

11. Security, Compliance, and Observability

  • SSH keys: Use unique keys per deploy user; rotate quarterly
  • Firewall: Only ports 22, 80, 443 open. Cloud SQL ports locked down.
  • Secrets: No secrets in code or Docker images; use runtime injection
  • Compliance: Audit all deploys, enforce encrypted backups, log all database access
  • Monitoring: Use UptimeRobot, Rollbar, or Honeybadger for errors; Prometheus + Grafana or ScoutAPM for metrics

12. Faith-Tech Perspective: DevOps as Ministry

Deploying faith apps isn’t just about uptime; it’s about trust, stewardship, and service. Here’s how DevOps practices become spiritual practices:

  • Preparation: “Full backup before deploy” = “Pray before big decisions”
  • Transparency: Every deploy is logged, every error is surfaced—modeling honesty
  • Rollbacks: Grace for mistakes; always a way back
  • Sabbath: No deploys on Sundays or late Fridays (rest is sacred)
  • Celebration: Share “deploy blessings” when things go well; encourage the team

Ember’s Tip: “A deploy well-done is an act of care for your community. It’s the difference between a safe nest and a leaky roof.”


13. Checklist & Resources

Deployment Readiness Checklist

  • [ ] Rails 8.0.1+ and Ruby 3.4.1+ installed
  • [ ] All gems and Node/Yarn dependencies updated
  • [ ] Dockerfile builds and runs cleanly in production mode
  • [ ] Database.yml uses ENV vars for all credentials
  • [ ] Kamal config (kamal.yml) complete and tested
  • [ ] SSH access to all target servers
  • [ ] Firewall configured for minimum exposure
  • [ ] Secrets managed outside codebase
  • [ ] Asset pipeline tested in Docker image
  • [ ] Healthcheck endpoint returns 200 OK
  • [ ] Rollback tested in staging before production
  • [ ] CI/CD pipeline builds, pushes, and deploys automatically

Further Reading & Tools


14. Closing Reflections

Kamal 2 and Rails 8 have made world-class deployment accessible to every developer, not just Fortune 500s or cloud-native startups. For faith-based teams, this is a game-changer: less time wrestling with infrastructure, more time serving your community.

Ember’s Farewell:
“Every reliable deploy is a prayer answered for your users. May your apps stay healthy, your deploys stay boring, and your mission shine through every line of code.” 🐧🔥

Written By Topher Warrington

Related Posts

Monitoring Without Madness: APM for Rails Apps

Monitoring Without Madness: APM for Rails Apps

Application Performance Monitoring (APM) is the secret weapon for keeping your Rails apps fast, reliable, and user-friendly. In “Monitoring Without Madness: APM for Rails Apps,” I break down how faith-based platforms like Prayer Nook use APM tools to diagnose bottlenecks, prevent errors, and build user trust. From tracking slow queries and background job failures to ensuring smooth page loads during peak traffic, this post offers a practical guide to observability.
You’ll learn how to choose the right APM tools (Scout, New Relic, or Honeybadger), set up monitoring for Rails 8 apps, and track key metrics like response times, error rates, and database performance. Real-world examples, including a case study from Prayer Nook, demonstrate how APM can cut prayer wall load times by 70% and boost user satisfaction. Plus, we’ll explore the ethical side of monitoring—how to balance data collection with user trust and privacy.
If you’re ready to stop guessing and start debugging with confidence, this post is your roadmap to building a monitoring strategy that works—without the madness.

read more
AI-Assisted Accessibility: How To Make Faith Apps for Everyone

AI-Assisted Accessibility: How To Make Faith Apps for Everyone

Inclusion is more than a checkbox—it’s a calling. In “AI-Assisted Accessibility: Making Faith Apps for Everyone,” I share how Prayer Nook and our ministry platforms leveraged AI to break down real barriers faced by users with disabilities, language differences, and diverse learning needs. With AI-powered voice input, instant spiritual translation, screen reader optimization, adaptive UIs, and empathetic audio guides, we’ve opened the door for elderly users, those with visual or motor challenges, and non-English speakers to fully participate in our digital faith communities.
This post goes beyond technical checklists to reveal the human stories behind accessibility: Anna, who can now pray aloud despite arthritis; Maria, whose Portuguese prayer reached an English-speaking friend; Sam, who found focus through a neurodivergent-friendly “simple mode.” Alongside code samples and real-world lessons, you’ll find practical Rails 8 integration patterns, prompt engineering for spiritual nuance, and honest talk about the ethical limits of AI.
The journey hasn’t been perfect—accents stumped our models, AI hallucinated scripture, and early TTS voices sounded robotic—but persistent iteration, transparency, and user feedback kept us moving forward. Most importantly, we learned that AI is a tool, not a replacement for human discernment or compassion. Accessibility, powered by AI, is about building ramps—digital and spiritual—so everyone can belong, participate, and be transformed.
If you’re building ministry or community software, this is your roadmap for making tech a true bridge, not a barrier. Let’s keep widening the circle—together.

read more

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *