In 2023, DHH (creator of Ruby on Rails and co-founder of 37signals) introduced Kamal — a deployment tool born from 37signals’ decision to leave the cloud and run their own servers. Kamal (originally called MRSK) is designed to deploy Docker containers to any server — cloud VM, bare metal, or Raspberry Pi — with zero-downtime rolling deploys.
Kamal is not a PaaS. It is a deployment tool — think of it as a modern Capistrano for the Docker era. You own the servers, Kamal handles the deployment.
Table of Contents
Open Table of Contents
What Is Kamal?
Kamal is an open-source deployment tool that uses Docker to deploy web applications to any server with SSH access. It handles building Docker images, pushing them to a registry, pulling them on your servers, and performing zero-downtime rolling deployments using Kamal Proxy (a custom reverse proxy built by 37signals).
Official page: https://kamal-deploy.org
GitHub: https://github.com/basecamp/kamal
Key Features
- Zero-downtime deploys — rolling deployments with health checks.
- Multi-server support — deploy to multiple servers simultaneously.
- Kamal Proxy — built-in reverse proxy with automatic SSL (Let’s Encrypt).
- Docker-native — builds and deploys standard Docker images.
- Accessory management — run databases, Redis, and other services alongside your app.
- Rolling restarts — update configuration without rebuilding images.
- Remote command execution — run commands inside containers on remote servers.
- Secret management — inject secrets from environment variables or vaults.
- Multi-app support — deploy multiple apps to the same servers.
- Works anywhere — any Linux server with SSH and Docker.
Getting Started — How to Install
Prerequisites
- One or more Linux servers with SSH access.
- Docker installed on your local machine (for building images).
- A Docker registry (Docker Hub, GitHub Container Registry, or self-hosted).
- Ruby 3.0+ (Kamal is a Ruby gem) or use the Docker-based installer.
Installation
# Option 1: Install as a Ruby gem
gem install kamal
# Option 2: Via Docker (no Ruby required)
alias kamal='docker run -it --rm \
-v "${PWD}:/workdir" \
-v "${SSH_AUTH_SOCK}:/ssh-agent" \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "SSH_AUTH_SOCK=/ssh-agent" \
ghcr.io/basecamp/kamal:latest'
Initialize a Project
kamal init
This creates a config/deploy.yml file — the heart of Kamal configuration.
Configuration: config/deploy.yml
service: my-saas-api
image: myorg/my-saas-api
servers:
web:
hosts:
- 203.0.113.1
- 203.0.113.2
labels:
role: web
worker:
hosts:
- 203.0.113.3
cmd: bundle exec sidekiq
proxy:
ssl: true
host: api.mycompany.com
registry:
server: ghcr.io
username: myorg
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
RAILS_ENV: production
DB_HOST: 203.0.113.4
secret:
- RAILS_MASTER_KEY
- DATABASE_URL
- REDIS_URL
accessories:
db:
image: postgres:16
host: 203.0.113.4
port: 5432
env:
clear:
POSTGRES_DB: myapp_production
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7
host: 203.0.113.4
port: 6379
directories:
- data:/data
Deploy
# First-time setup (installs Docker, Kamal Proxy, and deploys)
kamal setup
# Subsequent deploys
kamal deploy
Common Commands
# View app logs
kamal app logs
# Run a command in the container
kamal app exec "bin/rails console"
# Rollback to the previous version
kamal rollback
# View deployment details
kamal details
# Restart the app without rebuilding
kamal app boot
# Manage accessories
kamal accessory boot db
kamal accessory logs db
Adoption Level (2025–2026)
Kamal has gained significant traction thanks to DHH’s influence and the broader “leave the cloud” movement:
| Metric | Value (as of early 2026) |
|---|---|
| GitHub Stars | ~12,000+ |
| Creator | DHH / 37signals |
| First release | 2023 (as MRSK) |
| Used in production | HEY, Basecamp, ONCE |
| Built into Rails | Default in Rails 8 |
| Community | Active GitHub, Ruby community |
Why Kamal is gaining momentum:
- DHH’s endorsement — Rails 8 ships with Kamal as the default deployment tool.
- “Leave the cloud” movement — companies are saving millions by moving to bare metal.
- 37signals runs HEY and Basecamp on Kamal — real production validation.
- Works with any language — despite being a Ruby gem, it deploys any Docker container.
- Simple mental model — SSH + Docker + reverse proxy. No Kubernetes, no orchestrator.
Where Kamal falls short:
- No web UI — entirely CLI and config-file driven.
- No auto-scaling — you manually add/remove servers.
- Server management is your responsibility — OS updates, security patches, monitoring.
- Ruby dependency — non-Ruby teams may prefer a language-agnostic tool.
- No built-in log aggregation or monitoring — you need external tools (Grafana, Datadog).
Best Examples for Implementing
1. Rails 8 Application (Default Setup)
Rails 8 includes Kamal out of the box:
rails new my-app
cd my-app
# config/deploy.yml is already generated
kamal setup
2. Node.js API
# config/deploy.yml
service: node-api
image: myorg/node-api
servers:
web:
hosts:
- 203.0.113.1
proxy:
ssl: true
host: api.mycompany.com
registry:
server: ghcr.io
username: myorg
password:
- KAMAL_REGISTRY_PASSWORD
builder:
dockerfile: Dockerfile
env:
clear:
NODE_ENV: production
PORT: 3000
secret:
- DATABASE_URL
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
3. Multi-Server Production Setup
servers:
web:
hosts:
- 203.0.113.1 # Web server 1
- 203.0.113.2 # Web server 2
- 203.0.113.3 # Web server 3
worker:
hosts:
- 203.0.113.4 # Worker server
cmd: node worker.js
accessories:
db:
image: postgres:16
host: 203.0.113.5 # Dedicated DB server
port: 5432
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7
host: 203.0.113.5
port: 6379
Kamal deploys to all servers in parallel with zero-downtime rolling updates.
4. Python Django + Celery
service: django-app
image: myorg/django-app
servers:
web:
hosts:
- 203.0.113.1
cmd: gunicorn myproject.wsgi --bind 0.0.0.0:8000
worker:
hosts:
- 203.0.113.2
cmd: celery -A myproject worker --loglevel=info
beat:
hosts:
- 203.0.113.2
cmd: celery -A myproject beat --loglevel=info
proxy:
ssl: true
host: app.mycompany.com
accessories:
db:
image: postgres:16
host: 203.0.113.3
port: 5432
redis:
image: redis:7
host: 203.0.113.3
port: 6379
5. Go Microservice
service: go-service
image: myorg/go-service
servers:
web:
hosts:
- 203.0.113.1
builder:
dockerfile: Dockerfile
proxy:
ssl: true
host: service.mycompany.com
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /service .
FROM alpine:3.19
COPY --from=builder /service /service
EXPOSE 8080
CMD ["/service"]
Cost Analysis
Kamal is completely free and open-source. Your costs are servers and a Docker registry.
Server Costs (Bare Metal / VPS)
| Provider | 2 vCPU / 4 GB | 4 vCPU / 8 GB | Dedicated (8 core / 32 GB) |
|---|---|---|---|
| Hetzner | €4.50/month | €8.50/month | €39/month |
| DigitalOcean | $12/month | $24/month | — |
| OVH | €6/month | €12/month | €30/month |
| Vultr Bare Metal | — | — | $120/month |
Docker Registry
| Registry | Free Tier | Paid |
|---|---|---|
| Docker Hub | 1 private repo free | $5/month (unlimited) |
| GitHub Container Registry | Free for public repos | Included in GitHub plans |
| Self-hosted (Harbor) | Free | Your server cost |
Real-World Estimate for a Startup
A production setup with:
- 2 web servers (Hetzner CX22) — €9/month
- 1 worker server (Hetzner CX22) — €4.50/month
- 1 DB server (Hetzner CX32) — €8.50/month
- GitHub Container Registry — $0
Total: ~$24/month — for a production-grade, multi-server deployment with zero downtime.
37signals’ Cost Savings
37signals (the company behind Kamal) reported saving over $1 million per year by leaving AWS and moving to their own hardware managed with Kamal. While most startups will not see savings at that scale, the principle applies: owning your servers is cheaper at scale.
When Kamal Makes Sense
- Teams with 2+ servers that want automated, zero-downtime deploys.
- Rails shops (Kamal is now the default Rails deployment tool).
- Companies moving from cloud to bare metal / VPS.
- Any team comfortable with SSH and Docker.
When Kamal Is Overkill
- Single small apps — Dokku is simpler.
- Teams that want a GUI — use Coolify instead.
- Early MVPs — Railway or Render is faster to get started.
Verdict
Kamal is the best deployment tool for teams that own their servers. It brings the sophistication of container orchestration (rolling deploys, health checks, zero downtime) without the complexity of Kubernetes. If you are running Docker containers on VPS or bare-metal servers and want a battle-tested deployment workflow, Kamal — the tool that powers Basecamp and HEY — is the gold standard.
TL;DR: Kamal is what happens when 37signals gets frustrated with cloud costs and builds a deployment tool. It is opinionated, practical, and production-proven. If you own your servers and want zero-downtime Docker deploys, Kamal is the answer.
Follow my blog for more reviews of modern deployment platforms for startups.