Rails 8 deployen met Kamal 2: Een complete productie-setup vanaf nul
Kamal 2 is de standaard deployment-tool in Rails 8 en vervangt de oude Capistrano-workflow waar de meeste van ons mee zijn opgegroeid. Na het migreren van drie productie-apps van Capistrano naar Kamal in de afgelopen maanden, is hier de complete handleiding — inclusief de onderdelen die de documentatie overslaat.
Wat Kamal 2 precies doet
Kamal gebruikt Docker-containers die via SSH worden gedeployd. Geen Kubernetes. Geen managed container service. Je wijst het naar een server, het bouwt je Docker-image, pusht het naar een registry, pullt het op de server en wisselt verkeer via Kamal Proxy (dat Traefik uit Kamal 1 vervangt).
Het mentale model: Capistrano deployde code naar een server en herstartte processen. Kamal deployt containers naar een server en wisselt verkeer. De server heeft geen Ruby, Node of andere dependencies nodig — alleen Docker.
Vereisten
Je hebt nodig:
- Een server met Ubuntu 22.04+ en SSH-toegang (een VPS van €10/maand werkt prima)
- Een Docker Hub-account (of een andere container registry)
- Rails 8.0+ met Docker-ondersteuning (nieuwe apps hebben dit automatisch; bestaande apps hebben een
Dockerfilenodig) - Ruby 3.3+ lokaal
Bij een bestaande Rails 7-app, draai rails app:update na het upgraden naar Rails 8. Dit genereert de Dockerfile, .dockerignore en config/deploy.yml die je nodig hebt.
Stap 1: Installeer Kamal
gem install kamal
Rails 8-apps hebben het al in de Gemfile staan. Controleer met:
kamal version
# => 2.4.0
Stap 2: Configureer deploy.yml
De gegenereerde config/deploy.yml heeft echte waarden nodig. Hier is een uitgeklede productie-configuratie:
service: myapp
image: yourdockerhub/myapp
servers:
web:
hosts:
- 203.0.113.42
options:
memory: 512m
proxy:
ssl: true
host: myapp.com
registry:
username: yourdockerhub
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
RAILS_LOG_TO_STDOUT: "1"
RAILS_SERVE_STATIC_FILES: "1"
secret:
- RAILS_MASTER_KEY
- DATABASE_URL
builder:
arch: amd64
accessories:
db:
image: postgres:16
host: 203.0.113.42
port: "127.0.0.1:5432:5432"
env:
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
Een paar dingen die de docs onvoldoende benadrukken:
Geheugenlimieten zijn belangrijk. Zonder memory: 512m kan een enkel request dat veel geheugen alloceert al je VPS-RAM opeten, waarna de OOM-killer je container (en soms de database) afschiet. Stel dit in op basis van het daadwerkelijke geheugenprofiel van je app — check wat je GC-getunede Rails-proces werkelijk gebruikt.
Bind Postgres aan localhost. De 127.0.0.1:5432:5432 mapping houdt je database van het publieke internet af. Ik heb Kamal-tutorials gezien die de database-poort blootstellen zonder dit — dat is vragen om problemen op een €10 VPS zonder firewall.
Builder arch moet overeenkomen met je server. Als je ontwikkelt op Apple Silicon maar deployt naar een AMD64-server, stel dan arch: amd64 in. Zonder dit bouwt Kamal een ARM-image dat direct crasht op de server met een cryptische exec format error.
Stap 3: Secrets instellen
Maak een .kamal/secrets-bestand aan (niet committen naar git):
KAMAL_REGISTRY_PASSWORD=your_docker_hub_token
RAILS_MASTER_KEY=inhoud_van_config_master.key
DATABASE_URL=postgresql://myapp:geheimwachtwoord@myapp-db:5432/myapp_production
POSTGRES_PASSWORD=geheimwachtwoord
Kamal leest dit bestand automatisch tijdens deployment. Voor teamomgevingen kun je secrets ophalen uit 1Password, AWS SSM of elk commando dat KEY=VALUE-paren uitvoert.
Stap 4: Server voorbereiden
kamal server bootstrap
Dit SSHt naar je server(s) en installeert Docker. Op een verse Ubuntu VPS duurt het ongeveer 60 seconden.
Stel daarna de database-accessoire in:
kamal accessory boot db
Stap 5: Deploy
kamal deploy
De eerste deploy duurt 3-5 minuten (Docker-image bouwen, pushen, pullen op de server). Volgende deploys zijn sneller dankzij Docker layer caching — meestal 60-90 seconden als alleen je applicatiecode is gewijzigd.
Wat er gebeurt tijdens kamal deploy:
- Bouwt het Docker-image lokaal (of op een remote builder)
- Pusht het image naar je registry
- Pullt het image op elke server
- Draait
kamal deployhooks (indien geconfigureerd) - Start de nieuwe container
- Kamal Proxy health-checkt de nieuwe container
- Wisselt verkeer van oude naar nieuwe container
- Stopt de oude container
De verkeerswisseling is de grote verbetering ten opzichte van Capistrano. Er is geen moment waarop je app down is tijdens deployment.
Stap 6: Database-migraties
Kamal draait migraties niet automatisch. De aanbevolen aanpak:
kamal app exec --primary "bin/rails db:migrate"
kamal deploy
Dit geeft je de kans om te verifiëren dat de migratie is geslaagd voordat je verkeer wisselt. Voor zero-downtime migraties is dit de veiligste aanpak — draai je backward-compatible migratie, controleer het resultaat en deploy dan de code die ervan afhankelijk is.
Stap 7: SSL met Kamal Proxy
SSL-setup is automatisch:
proxy:
ssl: true
host: myapp.com
Kamal Proxy gebruikt Let’s Encrypt om certificaten te provisioneren en te vernieuwen. Wijs het DNS A-record van je domein naar het IP van je server, deploy, en SSL werkt. Geen certbot-configuratie, geen cron-jobs voor vernieuwing.
Let op: het eerste request na deployment kan traag zijn (2-3 seconden) omdat Kamal Proxy het certificaat on-demand provisioneert.
De valkuilen
Na een paar maanden Kamal 2 in productie, zijn dit de problemen die daadwerkelijk opdoken:
Docker-image groei. De standaard Rails 8 Dockerfile gebruikt multi-stage builds, maar je image kan alsnog opblazen als je niet oplet. Check met docker images — als je app-image groter is dan 500MB, kijk wat er wordt gekopieerd. Veelvoorkomende boosdoeners: node_modules in de finale stage, testbestanden, development gems. Een goed geoptimaliseerd Rails 8-image is 200-350MB.
Log-rotatie. Kamal stelt geen log-rotatie in. Docker’s standaard logging driver bewaart alles in JSON-bestanden die onbeperkt groeien. Voeg dit toe aan je deploy config:
servers:
web:
hosts:
- 203.0.113.42
options:
log-opt: max-size=50m
log-opt: max-file=3
Health check-fouten bij traag opstartende apps. Kamal Proxy checkt standaard /up. Als je Rails-app meer dan 7 seconden nodig heeft om op te starten, faalt de health check en wordt de deploy teruggedraaid. Verhoog de timeout:
proxy:
healthcheck:
interval: 3
timeout: 30
Monitoring en onderhoud
Handige commando’s na deployment:
# Bekijk logs
kamal app logs -f
# Open een Rails console
kamal app exec -i "bin/rails console"
# Bekijk containerstatus
kamal details
# Rollback naar vorige versie
kamal rollback
Kamal vs. Capistrano: wanneer overstappen
Stap over naar Kamal als:
- Je zero-downtime deploys wilt zonder Puma phased restarts te configureren
- Je nieuwe servers opzet en geen Ruby/Node-installaties wilt beheren
- Je een enkele tool wilt voor proxy, SSL en deployment
- Je een nieuwe Rails 8-app start (Kamal is de standaard — gebruik het gewoon)
Blijf bij Capistrano als:
- Je team het door en door kent en je deploy-pipeline solide is
- Je complexe multi-stage deploy-workflows hebt die afhankelijk zijn van Capistrano’s taaksysteem
- Je nog niet klaar bent om je app te containerizen
FAQ
Hoeveel kost een Kamal-deployment?
De tool zelf is gratis en open source. Je kosten zijn de VPS (€5-20/maand voor een kleine app) en de Docker registry (Docker Hub’s gratis tier geeft je één privé-repo, genoeg voor een enkele app). Geen managed Kubernetes-cluster, geen container orchestration-dienst.
Kan ik naar meerdere servers deployen met Kamal 2?
Ja. Voeg meer hosts toe onder de web-key in deploy.yml. Kamal deployt parallel naar alle servers. Je hebt een load balancer voor ze nodig (een simpele Nginx of HAProxy, of de load balancer van je cloudprovider).
Hoe ga ik om met background jobs in Kamal?
Definieer een aparte serverrol voor je background job processor:
servers:
web:
hosts:
- 203.0.113.42
worker:
hosts:
- 203.0.113.42
cmd: bundle exec sidekiq
Solid Queue-gebruikers kunnen het in hetzelfde proces als Puma draaien — geen aparte worker-container nodig.
Wat gebeurt er als een deploy faalt?
Kamal Proxy blijft verkeer naar de oude container sturen. De nieuwe container faalt zijn health check en wordt gestopt. Je app blijft draaien op de vorige versie. Draai kamal rollback om op te ruimen, los het probleem op en deploy opnieuw.
Kan ik van Capistrano naar Kamal migreren zonder downtime?
Ja, maar reken op 5-10 minuten setup. De basisaanpak: stel Kamal in gericht op dezelfde server, deploy je app naast de Capistrano-versie op een andere poort, verifieer dat het werkt, en wijs dan je DNS of reverse proxy naar de Kamal-container. Zodra verkeer via Kamal loopt, kun je de Capistrano-setup ontmantelen.
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 TouchRelated Articles
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