All articles

Deploying Rails With Kamal 2.0


Platforms like Heroku are convenient, but they get expensive fast — and you give up control. Kamal, the deploy tool that now ships with Rails, lets you deploy a containerised app to any server you can SSH into, with zero-downtime releases and automatic SSL. Version 2.0 made it dramatically simpler. Let’s ship a Rails app to a plain VPS.

Your app Rails + Dockerfile
Image kamal build
Registry Push the image
Servers Zero-downtime swap
Kamal builds a Docker image, pushes it to a registry, and rolls it out across your servers.
What is Kamal?

Kamal deploys web apps as Docker containers to your own servers. It SSHes in, pulls your image, boots the new container, health-checks it, and only then routes traffic to it — so users never see a blip. You own the infrastructure, but you get a Heroku-like git push workflow.

What’s new in 2.0
  • kamal-proxy: A purpose-built reverse proxy replaces Traefik. It handles zero-downtime cutovers and automatic HTTPS via Let’s Encrypt out of the box.
  • Easier multi-app hosting: Run several apps on one server without fighting over ports.
  • First-class secrets: A dedicated .kamal/secrets file with helpers to pull values from your environment or a password manager.
Install and initialise

Kamal ships with Rails 8. On older apps, add it and generate the config:

gem install kamal
kamal init

This creates config/deploy.yml, a .kamal/secrets file, and a Dockerfile (Rails 7.1+ already includes a production-ready one).

Configure the deploy

config/deploy.yml is the heart of it — name the service, the image, your servers, and the registry:

service: myapp
image: yourname/myapp

servers:
  web:
    - 192.168.0.1

proxy:
  ssl: true
  host: myapp.com

registry:
  username: yourname
  password:
    - KAMAL_REGISTRY_PASSWORD

env:
  secret:
    - RAILS_MASTER_KEY
  clear:
    RAILS_ENV: production

Setting proxy.ssl: true with a host is all it takes for kamal-proxy to provision and renew a Let’s Encrypt certificate.

Manage secrets

In 2.0, secrets live in .kamal/secrets (never commit the resolved values). You can read them straight from the environment or a tool like 1Password:

# .kamal/secrets
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$(cat config/master.key)
Your first deploy

Provision the servers (installs Docker, boots the proxy) once, then deploy:

kamal setup

After that, every release is a single command:

kamal deploy

Kamal builds the image, pushes it, pulls it on each server, runs your bin/docker-entrypoint (migrations included), health-checks the new container, and swaps traffic over with no downtime.

Day-to-day commands
kamal app logs -f        # tail logs
kamal console            # rails console on the server
kamal rollback           # revert to the previous release
kamal app exec "bin/rails db:migrate"
Wrapping up

Kamal 2.0 hits a sweet spot: the simplicity of a PaaS with the cost and control of your own hardware. Once config/deploy.yml is set up, shipping is just kamal deploy — and automatic SSL plus zero-downtime cutovers mean you spend time building features, not babysitting servers.

More articles