35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English 35+ Years Experience Netherlands Based ⚡ Fast Response Times Ruby on Rails Experts AI-Powered Development Fixed Pricing Available Senior Architects Dutch & English
Kamal 2 Deploy Rails: Zero-Downtime Deployments Zonder Kubernetes

Kamal 2 Deploy Rails: Zero-Downtime Deployments Zonder Kubernetes

TTB Software
devops

Kubernetes lost problemen op die de meeste Rails-teams niet hebben. Je draait twee app-servers, een database en Redis. Je hebt geen container-orkestratieplatform nodig dat ontworpen is voor duizenden microservices.

Kamal 2, de deployment-tool die standaard bij Rails 8 zit, regelt zero-downtime deploys naar elke server waar je via SSH bij kunt. Geen clusterbeheer, geen YAML-wildgroei, geen managed Kubernetes-factuur die meer kost dan je servers.

Ik heb het afgelopen jaar drie klantapplicaties gemigreerd van Capistrano naar Kamal. Elke migratie duurde minder dan een dag. De deploys werden sneller, de configuratie eenvoudiger, en de teams hadden geen “deployment-expert” meer nodig om code te shippen.

Wat Kamal Precies Doet

Kamal gebruikt Docker en kamal-proxy (een lichtgewicht HTTP-proxy) om containers naar je servers te deployen. Tijdens een deploy:

  1. Bouwt het je Docker-image lokaal of op een remote builder
  2. Pusht het naar een container registry
  3. Start de nieuwe container op elke server
  4. Wacht tot health checks slagen
  5. Schakelt kamal-proxy om naar de nieuwe container
  6. Stopt de oude container

De switchover gebeurt per server zonder dropped requests. Geen load balancer herconfiguratie, geen rolling restart scripts.

Vereisten

Je hebt nodig:

  • Een server met Ubuntu 22.04+ (of elke Linux met Docker-ondersteuning)
  • SSH-toegang met key-based authenticatie
  • Een container registry (Docker Hub, GitHub Container Registry, of een private registry)
  • Ruby 3.2+ en Rails 8+ lokaal

Installatie en Eerste Setup

Kamal zit standaard in nieuwe Rails 8 apps. Voor bestaande apps:

bundle add kamal
bundle exec kamal init

Dit genereert twee bestanden: config/deploy.yml en .kamal/secrets.

deploy.yml Configureren

Hier is een productiewaardige configuratie voor een typische Rails-app met Sidekiq:

service: myapp

image: your-registry/myapp

servers:
  web:
    hosts:
      - 203.0.113.10
      - 203.0.113.11
    labels:
      kamal-proxy.http.response_timeout: 30s
    options:
      memory: 1g
  worker:
    hosts:
      - 203.0.113.12
    cmd: bundle exec sidekiq -q default -q mailers
    options:
      memory: 2g

proxy:
  ssl: true
  host: myapp.com
  app_port: 3000
  healthcheck:
    interval: 3
    path: /up
    timeout: 5

registry:
  server: ghcr.io
  username: your-github-user
  password:
    - KAMAL_REGISTRY_PASSWORD

builder:
  arch: amd64
  cache:
    type: registry
    image: your-registry/myapp-build-cache

env:
  clear:
    RAILS_LOG_TO_STDOUT: "1"
    RAILS_SERVE_STATIC_FILES: "true"
  secret:
    - RAILS_MASTER_KEY
    - DATABASE_URL
    - REDIS_URL

accessories:
  redis:
    image: redis:7-alpine
    host: 203.0.113.12
    port: 6379
    directories:
      - data:/data
    cmd: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru

Een paar dingen om op te letten:

Het proxy-blok vervangt Traefik uit Kamal 1. kamal-proxy regelt SSL-certificaten via Let’s Encrypt automatisch. Zet ssl: true en wijs je DNS — de rest gaat vanzelf.

De builder cache bespaart flink wat tijd bij volgende deploys. Zonder registry-caching herbouwt elke deploy alle Docker-layers. Met caching worden alleen gewijzigde layers opnieuw gebouwd. Dit bracht deploytijden terug van 8 minuten naar minder dan 2 minuten voor een middelgrote Rails-app.

Memory limits voorkomen dat wegloperprocessen de server platleggen. Stel ze in op basis van het daadwerkelijke gebruik van je app, plus 20% ruimte.

Secrets Instellen

Bewerk .kamal/secrets om credentials uit je omgeving of een secret manager te halen:

KAMAL_REGISTRY_PASSWORD=$GITHUB_TOKEN
RAILS_MASTER_KEY=$(cat config/master.key)
DATABASE_URL=$PRODUCTION_DATABASE_URL
REDIS_URL=$PRODUCTION_REDIS_URL

Kamal leest dit bestand tijdens deployment en injecteert de waarden als omgevingsvariabelen in je containers. Het bestand zelf wordt nooit naar de server gekopieerd.

Je Dockerfile

Rails 8 genereert een productiewaardige Dockerfile. Als je upgradet, ziet een goede er zo uit:

FROM ruby:3.3-slim AS base
WORKDIR /rails
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle"

FROM base AS build
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y \
    build-essential git libpq-dev node-gyp pkg-config python-is-python3
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache
COPY . .
RUN bundle exec rails assets:precompile
RUN rm -rf node_modules

FROM base
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y \
    curl libpq5 && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER 1000:1000
ENTRYPOINT ["./bin/docker-entrypoint"]
EXPOSE 3000
CMD ["bundle", "exec", "thrust", "./bin/rails", "server"]

De multi-stage build houdt het uiteindelijke image klein. De build-stage installeert compilatie-afhankelijkheden, bouwt alles, en de productie-stage kopieert alleen de gecompileerde artefacten. Een typische Rails-app image wordt zo rond de 250-350MB.

Eerste Deploy

kamal setup

Dit commando doet alles: Docker installeren op je servers indien nodig, kamal-proxy opzetten, je image pullen en de app starten. De eerste deploy duurt langer omdat de server wordt ingericht.

Bekijk de output. Kamal laat precies zien wat het op elke server uitvoert. Als iets faalt, zie je het exacte commando en de fout.

Volgende Deploys

kamal deploy

Dat is alles. Bouwen, pushen, nieuwe container starten, health check, verkeer omleiden, oude container stoppen. Typische deploytijd voor een Rails-app: 1-3 minuten afhankelijk van imagegrootte en cache-hits.

Health Checks Zijn Cruciaal

Kamal schakelt pas verkeer om als de health check slaagt. Het standaard Rails health check endpoint op /up werkt:

# config/routes.rb
Rails.application.routes.draw do
  get "up" => "rails/health#show", as: :rails_health_check
end

Als je app databaseconnectiviteit nodig heeft om “gezond” te zijn, pas dan de health check aan:

# app/controllers/health_controller.rb
class HealthController < ApplicationController
  def show
    ActiveRecord::Base.connection.execute("SELECT 1")
    render plain: "OK"
  rescue StandardError => e
    render plain: "FAIL: #{e.message}", status: :service_unavailable
  end
end

Een slechte health check configuratie is de meest voorkomende bron van deploymentproblemen. Te strikt (vereist alle externe services) en deploys falen bij elke hickup van een dependency. Te los (retourneert gewoon 200) en je routeert verkeer naar kapotte containers.

Check of de app opstart en requests kan afhandelen. Dat is de juiste drempel.

Migraties Uitvoeren

Kamal draait migraties niet automatisch, en dat is bewust. Databasemigraties en code-deployments zijn aparte zaken.

kamal app exec --roles=web "bin/rails db:migrate"
kamal deploy

Draai migraties vóór deployment wanneer je nieuwe tabellen of kolommen toevoegt. Draai ze ná deployment wanneer je kolommen verwijdert (de oude code verwijst er nog naar tijdens de switchover).

Gebruik voor zero-downtime migraties specifiek de strong_migrations gem om gevaarlijke migraties te detecteren voordat ze productie raken.

Debuggen van Productieproblemen

Kamal geeft je directe toegang tot je draaiende containers:

# Logs bekijken
kamal app logs -f

# Rails console openen
kamal app exec -i "bin/rails console"

# Container resource-gebruik checken
kamal app exec "cat /proc/1/status | grep VmRSS"

# Een eenmalig commando draaien
kamal app exec "bin/rails runner 'puts User.count'"

Vergelijk dit met Kubernetes, waar je kubectl exec -it deployment/myapp -- bin/rails console nodig hebt na het configureren van contexts, namespaces en RBAC. Kamal houdt de mentale overhead laag.

Wanneer Kamal Niet Genoeg Is

Kamal werkt goed voor de meeste Rails-deployments. Het begint te piepen wanneer je nodig hebt:

  • Auto-scaling: Kamal deployt naar een vaste set servers. Als je moet schalen van 2 naar 20 instanties op basis van verkeer, heb je iets anders nodig (of scripting rond Kamal + de API van je cloud provider).
  • Multi-regio failover: Kamal handelt geen geografische routing af. Je zou een CDN of DNS-gebaseerde failover ervoor plaatsen.
  • Complexe service meshes: Als je architectuur 15 services heeft die service discovery en circuit breaking nodig hebben, verdient Kubernetes zijn complexiteit.

Voor een Rails-monoliet met een background job processor en misschien een aparte service of twee, handelt Kamal alles af wat je nodig hebt.

Migreren vanaf Capistrano

Het migratiepad is rechttoe rechtaan:

  1. Voeg een Dockerfile toe en verifieer dat het lokaal bouwt
  2. Maak config/deploy.yml aan met je bestaande server-IP’s
  3. Draai kamal setup op een staging-server eerst
  4. Test grondig, vooral background jobs en file storage
  5. Wijs DNS naar de Kamal-beheerde servers
  6. Draai kamal setup op productie

De grootste aanpassing is mentaal. Met Capistrano leeft je code op de server en denk je in termen van releases, shared directories en symlinks. Met Kamal is je app een container image, en de server is gewoon een host die het draait. Logs gaan naar stdout. Bestanden gaan naar object storage. Omgevingsvariabelen vervangen gedeelde configuratiebestanden.

Afrondend

Kamal geeft Rails-teams een deployment-tool die past bij de filosofie van het framework: convention over configuration, verstandige defaults, en uit je weg blijven. Je krijgt zero-downtime deploys, automatische SSL en containergebaseerde reproduceerbaarheid zonder de operationele belasting van Kubernetes.

Eén keer instellen. Deployen met één commando. Besteed je tijd aan features bouwen in plaats van infrastructuur beheren.

#kamal #rails #deployment #devops #docker #zero-downtime #production
T

About the Author

Roger Heykoop is een senior Ruby on Rails ontwikkelaar met 19+ jaar Rails ervaring en 35+ jaar ervaring in softwareontwikkeling. Hij is gespecialiseerd in Rails modernisering, performance optimalisatie, en AI-ondersteunde ontwikkeling.

Get in Touch

Share this article

Need Expert Rails Development?

Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise

Schedule a Free Consultation