Awesome-omni-skill kamal-deploy
Expert-level Kamal deployment guidance for deploying containerized applications to any server. Use this skill when users ask about Kamal, container deployment, zero-downtime deployments, deploying Rails/web apps to VPS/cloud servers, kamal setup, kamal deploy, Docker deployment without Kubernetes, or deploying to Hetzner/DigitalOcean/AWS with Kamal. Also use when users mention DHH's deployment tool, 37signals deployment, or want an alternative to Heroku/Render/Vercel with self-hosted infrastructure.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/devops/kamal-deploy" ~/.claude/skills/diegosouzapw-awesome-omni-skill-kamal-deploy && rm -rf "$T"
skills/devops/kamal-deploy/SKILL.mdKamal Deploy Expert
Expert guidance for deploying applications with Kamal - DHH's zero-downtime deployment tool from 37signals.
Step 1: Fetch Latest Documentation (MANDATORY)
BEFORE answering ANY Kamal question, you MUST use the WebFetch tool to get current documentation. The docs below may be outdated - always fetch fresh docs first.
Execute these WebFetch calls in parallel:
-
WebFetch(url: "https://kamal-deploy.org/docs/installation/", prompt: "Extract complete installation and setup guide") -
WebFetch(url: "https://kamal-deploy.org/docs/configuration/overview/", prompt: "Extract all configuration options and deploy.yml structure") -
WebFetch(url: "https://kamal-deploy.org/docs/commands/view-all-commands/", prompt: "Extract all Kamal commands and usage") -
WebFetch(url: "https://kamal-deploy.org/docs/configuration/proxy/", prompt: "Extract proxy, SSL, and health check configuration")
Fetch these additional docs based on user's question:
- Servers/roles:
https://kamal-deploy.org/docs/configuration/servers/ - Accessories (DB, Redis):
https://kamal-deploy.org/docs/configuration/accessories/ - Environment variables:
https://kamal-deploy.org/docs/configuration/environment-variables/ - Docker build options:
https://kamal-deploy.org/docs/configuration/builders/ - Deployment hooks:
https://kamal-deploy.org/docs/hooks/overview/ - Upgrading v1→v2:
https://kamal-deploy.org/docs/upgrading/overview/
Only after fetching fresh docs, use the reference material below as supplementary context.
What is Kamal?
Kamal deploys containerized apps to any server via SSH + Docker. Created by 37signals (DHH's company) to deploy Basecamp, HEY, and other apps.
Core architecture:
- SSHs into servers, installs Docker automatically
- Builds app into Docker container
- Pushes to registry (Docker Hub, GHCR, etc.)
- Pulls and runs on target servers
- kamal-proxy handles routing, SSL (Let's Encrypt), zero-downtime
Mental model: Hetzner/DigitalOcean = the computer, Kamal = deploys your app to it
Before You Start
Check these first to avoid common friction:
-
Kamal version - Run
. If on 1.x, upgrade withkamal version
. Config syntax changed significantly (1.x usesgem install kamal
, 2.x usestraefik
).proxy -
Local Docker situation - Ask the user if they have Docker working locally. If not (or if Docker Desktop is problematic on macOS), configure a remote builder:
builder: arch: amd64 remote: ssh://root@SERVER_IPThis builds on the target server and avoids local Docker entirely.
-
37signals open-source repos - If deploying Campfire, HEY, or other 37signals apps, immediately delete
- it uses their internal 1Password setup and will fail with.env.erb
.op: command not found -
Registry access - Confirm the user has a container registry (Docker Hub, GHCR) and knows their credentials before writing config.
Quick Start
# Install (or upgrade) gem install kamal # Initialize in project kamal init # First deploy (installs Docker, proxy, deploys app) kamal setup # Subsequent deploys kamal deploy
Essential Commands
| Command | Purpose |
|---|---|
| First deploy - installs Docker, proxy, deploys |
| Deploy new version |
| Revert to previous version |
| View application logs |
| SSH into running container |
| Start accessory (db, redis) |
| Restart kamal-proxy |
| Remove everything from servers |
Minimal config/deploy.yml
service: my-app image: username/my-app servers: - 123.45.67.89 registry: username: username password: - KAMAL_REGISTRY_PASSWORD proxy: ssl: true host: myapp.com env: secret: - RAILS_MASTER_KEY - DATABASE_URL
Secrets Management
Secrets live in
.kamal/secrets:
# .kamal/secrets KAMAL_REGISTRY_PASSWORD=ghp_xxxxxxxxxxxx RAILS_MASTER_KEY=abc123def456 DATABASE_URL=postgres://user:pass@db:5432/app
Reference in deploy.yml:
env: clear: RAILS_ENV: production secret: - RAILS_MASTER_KEY - DATABASE_URL
Multi-Server with Roles
servers: web: hosts: - 123.45.67.89 - 123.45.67.90 workers: hosts: - 123.45.67.91 cmd: bin/jobs proxy: false # Workers don't need proxy
Accessories (Databases, Redis)
accessories: db: image: postgres:16 host: 123.45.67.89 port: 5432 env: clear: POSTGRES_DB: app_production secret: - POSTGRES_PASSWORD directories: - data:/var/lib/postgresql/data redis: image: redis:7 host: 123.45.67.89 port: 6379 directories: - data:/data
SSL Configuration
Automatic (Let's Encrypt):
proxy: ssl: true host: myapp.com # Must point to server IP
Custom certificate:
proxy: ssl: certificate_pem: - SSL_CERTIFICATE private_key_pem: - SSL_PRIVATE_KEY
Health Checks
proxy: healthcheck: interval: 3 path: /up timeout: 3
App must return 200 on
/up (Rails default) or configured path.
Destinations (Staging/Production)
Create
config/deploy.staging.yml:
servers: - staging.myapp.com proxy: host: staging.myapp.com
Deploy:
kamal deploy -d staging
Secrets:
.kamal/secrets.staging
Hooks
Place in
.kamal/hooks/ (no file extension):
Available hooks:
,pre-connect
,pre-build
,pre-deploypost-deploy
,pre-app-bootpost-app-boot
,pre-proxy-rebootpost-proxy-reboot
Example
.kamal/hooks/post-deploy:
#!/bin/bash curl -X POST "https://api.honeybadger.io/v1/deploys" \ -d "deploy[revision]=$KAMAL_VERSION"
Dockerfile Requirements
Kamal needs a Dockerfile. For Rails:
FROM ruby:3.3-slim WORKDIR /app # Install dependencies RUN apt-get update -qq && apt-get install -y build-essential libpq-dev COPY Gemfile* ./ RUN bundle install COPY . . RUN bundle exec rails assets:precompile EXPOSE 80 CMD ["bin/rails", "server", "-b", "0.0.0.0", "-p", "80"]
Note: Kamal 2.x defaults to port 80 (not 3000).
Common Issues
"Container not healthy"
- Check
endpoint returns 200/up - Increase
if app boots slowlydeploy_timeout - Check logs:
kamal app logs
"Permission denied"
- Ensure SSH key is added:
ssh-add ~/.ssh/id_rsa - Check SSH user has Docker access
Registry auth failed
- Verify
inKAMAL_REGISTRY_PASSWORD.kamal/secrets - For GHCR: use personal access token with
write:packages
"Address already in use"
- Another service on port 80/443
- Run
or checkkamal proxy reboot
on serverdocker ps
Kamal vs Alternatives
| Kamal | Kubernetes | Heroku | |
|---|---|---|---|
| Complexity | Low | High | None |
| Cost | VPS only | VPS + overhead | $$$ |
| Control | Full | Full | Limited |
| Zero-downtime | Yes | Yes | Yes |
| SSL | Auto | Manual | Auto |
| Learning curve | Hours | Weeks | Minutes |
Best Practices
- Always test locally first:
docker build . && docker run -p 3000:80 <image> - Use staging destination before production
- Keep secrets out of git:
in.kamal/secrets.gitignore - Set up monitoring: Use hooks to notify on deploy
- Regular backups: Especially accessory volumes
- Use asset bridging for Rails:
asset_path: /app/public/assets
Reference Files
For detailed configuration options, see:
- references/configuration.md - Complete deploy.yml reference
- references/troubleshooting.md - Common issues and solutions