Overview
Kamal is 37signals’ deployment tool (powers Basecamp & HEY) designed for zero-downtime deployments to any VPS.
Best for: Teams needing zero-downtime deploys and preferring CLI over web UI.
Why Kamal?
- ✅ Zero-downtime deployments
- ✅ Simple YAML configuration
- ✅ Git-based workflow
- ✅ Built-in secret management
- ✅ Battle-tested by 37signals
Prerequisites
Local machine:
- Ruby 3.0+ (
ruby --version)
- Docker installed
- SSH access to VPS
VPS:
- Ubuntu 24.04 LTS
- Docker installed
- SSH key configured
Installation
1. Install Kamal
2. Initialize Configuration
In your ripplecore-forge repository:
This creates config/deploy.yml.
Edit config/deploy.yml:
service: ripplecore
image: your-username/ripplecore
servers:
web:
- 46.224.2.100
registry:
username: your-dockerhub-username
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
NODE_ENV: production
secret:
- DATABASE_URL
- REDIS_URL
- BETTER_AUTH_SECRET
ssh:
user: root
healthcheck:
path: /api/health
port: 3000
accessories:
postgres:
image: postgres:18-alpine
host: 46.224.2.100
port: 5432
env:
clear:
POSTGRES_DB: ripplecore
POSTGRES_USER: ripplecore
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
host: 46.224.2.100
port: 6379
cmd: --requirepass ${REDIS_PASSWORD}
directories:
- data:/data
traefik:
options:
publish:
- 443:443
volume:
- /letsencrypt:/letsencrypt
args:
entryPoints.web.address: ":80"
entryPoints.websecure.address: ":443"
certificatesResolvers.letsencrypt.acme.email: "your@email.com"
certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
certificatesResolvers.letsencrypt.acme.httpchallenge: true
certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
4. Set Environment Variables
Create .env:
KAMAL_REGISTRY_PASSWORD=your_dockerhub_token
DATABASE_URL=postgresql://ripplecore:PASSWORD@postgres:5432/ripplecore
REDIS_URL=redis://:PASSWORD@redis:6379
BETTER_AUTH_SECRET=your_secret
POSTGRES_PASSWORD=your_postgres_password
REDIS_PASSWORD=your_redis_password
Add .env to .gitignore!
Deployment
Initial Setup
# Set up accessories (PostgreSQL, Redis)
kamal accessory boot all
# Run migrations
kamal app exec -i "pnpm db:push"
# Deploy application
kamal deploy
Subsequent Deploys
# Zero-downtime deploy
kamal deploy
Kamal automatically:
- Builds new Docker image
- Pushes to registry
- Pulls on VPS
- Starts new container
- Health checks new container
- Stops old container (zero downtime!)
Commands
# Deploy latest code
kamal deploy
# View logs
kamal app logs --follow
# SSH into server
kamal app exec -i bash
# Restart app
kamal app restart
# Roll back to previous version
kamal app rollback
# View status
kamal details
# Stop application
kamal app stop
# Start PostgreSQL
kamal accessory boot postgres
# Backup database
kamal accessory exec postgres -- pg_dump -U ripplecore ripplecore
Configuration Details
Multiple Apps
For multiple services (app, api, web):
servers:
web:
hosts:
- 46.224.2.100
labels:
traefik.http.routers.app.rule: "Host(`app.yourdomain.com`)"
api:
hosts:
- 46.224.2.100
labels:
traefik.http.routers.api.rule: "Host(`api.yourdomain.com`)"
cmd: node apps/api/server.js
Custom Dockerfile
Specify in deploy.yml:
builder:
dockerfile: apps/app/Dockerfile
context: .
Migration from Dokploy
1. Export Dokploy Data
# Backup database
docker exec ripplecore-postgres pg_dump -U ripplecore > backup.sql
2. Deploy with Kamal
# Initialize and configure Kamal
kamal init
# Edit deploy.yml (see above)
# Boot accessories
kamal accessory boot all
# Deploy app
kamal deploy
3. Restore Data
cat backup.sql | kamal accessory exec postgres -i -- psql -U ripplecore ripplecore
4. Update DNS
Point to new VPS if different.
Migration time: 2-3 hours
Pros & Cons
Pros:
- ✅ Zero-downtime deploys
- ✅ Simple YAML config
- ✅ Git-based workflow
- ✅ Built-in secrets
- ✅ Battle-tested
Cons:
- ❌ Requires Ruby locally
- ❌ CLI-only (no GUI)
- ❌ Manual monitoring setup
- ❌ Fewer docs than Dokploy
Best Practices
- Use CI/CD: Run
kamal deploy from GitHub Actions
- Health Checks: Always configure health checks
- Backup: Automate database backups
- Test Locally: Use
kamal envify to test configs
Resources
Kamal is perfect for teams comfortable with CLI tools who need zero-downtime deployments.