Ruby on Rails 8 Kamal 2 Deployment on AWS EC2 & Hetzner: Complete Production Playbook
Deploying Ruby on Rails 8 with Kamal 2 on AWS EC2 or Hetzner offers a high-performance, cost-effective infrastructure without PaaS margins. By combining Docker, Let’s Encrypt SSL, and multi-server clustering, engineer...
Direct Answer: Zero-Downtime Deployment of Ruby on Rails 8 with Kamal 2
Deploying Ruby on Rails 8 with Kamal 2 on AWS EC2 or Hetzner offers a high-performance, cost-effective infrastructure without PaaS margins. By combining Docker, Let’s Encrypt SSL, and multi-server clustering, engineering teams achieve zero-downtime rolling deployments, native asset compilation, and absolute data sovereignty.
Welcome to the definitive engineering playbook from the Principal Solutions Architecture team at TechVinta. If you are tired of paying exorbitant platform-as-a-service (PaaS) fees for Heroku, Render, or Fly.io, this guide outlines how to orchestrate a modern production-grade deployment stack using Kamal 2, Docker, and bare-metal or cloud instances (AWS EC2 & Hetzner).
For US-based product engineering teams looking to modernize legacy infrastructure or build scalable enterprise platforms, TechVinta provides elite engineering services. We maintain a strict 4-to-6-hour US timezone overlap, ensuring seamless real-time collaboration, rapid incident response, and transparent architectural sprints.
1. Modern Infrastructure Architecture: Kamal 2 vs. PaaS
Rails 8 introduces a powerful suite of native tools—such as Solid Queue, Solid Cache, and Solid Cable—that eliminate the historical need for Redis and Sidekiq in many applications. When paired with Kamal 2, you no longer need complex Kubernetes clusters or expensive PaaS abstractions to manage containerized deployments. Kamal speaks directly to Docker on your remote servers via SSH, leveraging Accessory services (like MySQL, PostgreSQL, or Redis) and rolling accessory health checks.
Below is an architectural breakdown and financial comparison of deploying a production-grade Rails 8 application across cloud providers versus traditional agencies or PaaS environments.
| Metric / Component | Traditional PaaS (Render/Heroku) | Hetzner Cloud (Bare-Metal/Cloud) | AWS EC2 (Enterprise Multi-AZ) |
|---|---|---|---|
| Monthly Infrastructure Cost | $400 – $1,200+ | $35 – $65 | $120 – $300 |
| Estimated Setup Timeline | 2 – 4 Days | 1 – 2 Weeks (Full HA Setup) | 2 – 3 Weeks (VPC & ALB Setup) |
| Deployment Mechanism | Git Push / PaaS CLI | Kamal 2 (SSH + Docker) | Kamal 2 / GitHub Actions + AWS ECR |
| Data Sovereignty & Lock-in | High Lock-in | None (Pure Docker/Linux) | Moderate (AWS Ecosystem APIs) |
| Custom Development Rate | $150 – $250/hr (Agencies) | Optimized via TechVinta | $8,000 – $25,000 Project Sprints |
2. Prerequisites & Server Provisioning
Before initializing Kamal 2, prepare your target nodes. Whether you choose Hetzner Cloud (for maximum cost-efficiency) or AWS EC2 (for enterprise compliance and VPC integration), your server must meet the following baseline requirements:
- Ubuntu 24.04 LTS (Noble Numbat) or Debian 12 Bookworm.
- Docker Engine installed and configured with non-root user permissions (or root SSH access with pubkey authentication).
- A public IPv4 address and configured DNS A/AAAA records pointing to your target load balancer or web node.
- An external or accessory database instance (PostgreSQL 16 or MySQL 8) secured within a private subnet or internal Docker network.
3. Configuring Kamal 2 for Rails 8
Kamal 2 manages your application containers, environment variables, and Traefik reverse proxy configuration directly from your local development environment or CI/CD pipeline. Create or update your config/deploy.yml file to match this production-grade blueprint:
# Name of your application
service: techvinta-core
# Name of the container image
image: your-dockerhub-username/techvinta-core
# Deploy to these servers
servers:
web:
- 192.0.2.10 # Primary Web & Worker Node (Hetzner/AWS)
- 192.0.2.11 # Secondary Web & Worker Node (Clustering)
# Enable TLS via Traefik and Let's Encrypt automatically
proxy:
ssl: true
host: app.techvinta.com
app_port: 3000
healthcheck:
path: /up
interval: 5
timeout: 3
# Credentials for your image registry
registry:
username: your-dockerhub-username
password:
- KAMAL_REGISTRY_PASSWORD
# Inject secrets securely from .env file or CI environment
env:
clear:
DB_HOST: 192.0.2.50
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: "true"
secret:
- RAILS_MASTER_KEY
- DATABASE_URL
# Configure persistent volumes for logs or local storage if needed
volumes:
- "techvinta_storage:/rails/storage"
# Use accessory services if running MySQL/PostgreSQL locally on a dedicated node
accessories:
db:
image: postgres:16-alpine
host: 192.0.2.50
port: 5432
env:
POSTGRES_DB: techvinta_production
POSTGRES_USER: postgres
POSTGRES_PASSWORD:
- DB_PASSWORD
files:
- config/init.sql:/docker-entrypoint-initdb.d/init.sql
directories:
- data:/var/lib/postgresql/data
4. Handling Dockerfile & Multi-Stage Builds for Rails 8
Rails 8 ships with an optimized Dockerfile generator. Ensure your production Dockerfile leverages multi-stage builds to keep image sizes minimal and secure:
# syntax = docker/dockerfile:1
arg RUBY_VERSION=3.3.0
from docker.io/library/ruby:$RUBY_VERSION-slim as base
workdir /rails
run apt-get update -qq && \
apt-get install --no-install-recommends -y curl libjemalloc2 libsqlite3-0 libpq-dev && \
rm -rf /var/lib/ca-certificates /var/lib/apt/lists/*
env RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development:test"
from base as build
run apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libpq-dev pkg-config && \
rm -rf /var/lib/apt/lists/*
copy Gemfile Gemfile.lock ./
run bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/.git
copy . .
run bundle exec bootsnap precompile app/ lib/
from base
copy --from=build /usr/local/bundle /usr/local/bundle
copy --from=build /rails /rails
run useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
user rails:rails
entrypoint ["/rails/bin/docker-entrypoint"]
expose 3000
cmd ["./bin/rails", "server"]
5. Executing Zero-Downtime Deployments
With your configuration and Dockerfile validated, setting up your servers for the very first time requires a bootstrap command. Kamal 2 will install Docker, configure Traefik, set up SSL certificates via Let's Encrypt, and pull down your application container.
# Step 1: Initialize Docker and Traefik proxy on remote hosts
kamal setup
# Step 2: Deploy your application with zero downtime rolling updates
kamal deploy
When running kamal deploy, Traefik routes incoming traffic to existing running containers while new containers spin up, execute database migrations, and pass health checks (hitting your /up endpoint). Once healthy, traffic seamlessly switches over without dropping a single active HTTP connection.
Frequently Asked Questions
1. How does Kamal 2 handle database migrations during zero-downtime deployments?
Kamal 2 executes your database migrations inside a temporary container running the same image before swapping out the old web application containers. To achieve true zero-downtime safety, ensure your migrations adhere to backward-compatible practices (e.g., separating column additions from model logic updates across consecutive deployments).
2. Can I use Hetzner and AWS EC2 simultaneously within the same Kamal cluster?
Yes. Kamal is server-agnostic and relies purely on SSH access and Docker daemon endpoints. You can define multiple servers across distinct cloud providers under your servers.web configuration block, provided network security groups and firewalls allow internal communication between database accessories and web nodes.
3. How does TechVinta assist engineering teams with custom Rails 8 and Kamal migrations?
TechVinta partners with scaling startups and enterprises to audit legacy infrastructure, eliminate unnecessary PaaS licensing expenses, and build resilient deployment pipelines. With our expert engineering team operating on a transparent 4-to-6-hour US timezone overlap, we handle full-cycle cloud migrations, CI/CD automation, and custom React/Rails architecture.