Claude-skill-registry kamal-coder
This skill guides deploying Rails applications with Kamal. Use when configuring deploy.yml, setting up accessories, managing secrets, or preparing servers for container deployment.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/kamal-coder" ~/.claude/skills/majiayu000-claude-skill-registry-kamal-coder && rm -rf "$T"
manifest:
skills/data/kamal-coder/SKILL.mdsource content
Kamal Coder
Overview
Kamal deploys containerized applications to bare metal or VMs using Docker. It handles zero-downtime deployments with Traefik as reverse proxy.
Server Requirements
Before Kamal can deploy, servers need:
| Requirement | Purpose |
|---|---|
| Docker | Container runtime |
| SSH access | Kamal connects via SSH |
| Ports 80, 443 open | HTTP/HTTPS traffic |
| Port 22 open | SSH for deployments |
Provision with: Ansible (
infra/bin/provision --config) or cloud-init at boot time.
Configuration: config/deploy.yml
Minimal Setup
service: myapp image: username/myapp servers: web: hosts: - 192.168.1.1 labels: traefik.http.routers.myapp.rule: Host(`myapp.com`) registry: username: username password: - KAMAL_REGISTRY_PASSWORD env: clear: RAILS_ENV: production RAILS_LOG_TO_STDOUT: "true" secret: - RAILS_MASTER_KEY - DATABASE_URL
Multi-Role Setup
service: myapp image: username/myapp servers: web: hosts: - 192.168.1.1 - 192.168.1.2 labels: traefik.http.routers.myapp.rule: Host(`myapp.com`) worker: hosts: - 192.168.1.3 cmd: bundle exec sidekiq traefik: false # No HTTP traffic registry: username: username password: - KAMAL_REGISTRY_PASSWORD env: clear: RAILS_ENV: production secret: - RAILS_MASTER_KEY - DATABASE_URL - REDIS_URL
With Accessories (Databases, Redis)
service: myapp image: username/myapp servers: web: hosts: - 192.168.1.1 accessories: db: image: postgres:16 host: 192.168.1.1 port: 5432 env: clear: POSTGRES_DB: myapp_production secret: - POSTGRES_PASSWORD directories: - data:/var/lib/postgresql/data options: shm-size: 256m redis: image: redis:7-alpine host: 192.168.1.1 port: 6379 directories: - data:/data cmd: redis-server --appendonly yes
Secrets: .kamal/secrets
Kamal reads secrets from
.kamal/secrets (git-ignored).
With 1Password CLI
# .kamal/secrets KAMAL_REGISTRY_PASSWORD=$(op read "op://Infrastructure/DockerHub/password") RAILS_MASTER_KEY=$(op read "op://MyApp/production/master_key") DATABASE_URL=$(op read "op://MyApp/production/database_url") POSTGRES_PASSWORD=$(op read "op://MyApp/production-db/password")
With Environment Variables
# .kamal/secrets KAMAL_REGISTRY_PASSWORD=$DOCKERHUB_TOKEN RAILS_MASTER_KEY=$RAILS_MASTER_KEY DATABASE_URL=$DATABASE_URL
Multi-Environment
# config/deploy.yml <% if ENV["KAMAL_DESTINATION"] == "staging" %> service: myapp-staging <% else %> service: myapp <% end %>
# .kamal/secrets.staging RAILS_MASTER_KEY=$(op read "op://MyApp/staging/master_key")
Traefik Configuration
SSL with Let's Encrypt
traefik: options: publish: - "443:443" volume: - /letsencrypt:/letsencrypt args: entryPoints.web.address: ":80" entryPoints.websecure.address: ":443" entryPoints.web.http.redirections.entryPoint.to: websecure entryPoints.web.http.redirections.entryPoint.scheme: https certificatesResolvers.letsencrypt.acme.email: admin@myapp.com certificatesResolvers.letsencrypt.acme.storage: /letsencrypt/acme.json certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web servers: web: hosts: - 192.168.1.1 labels: traefik.http.routers.myapp.rule: Host(`myapp.com`) traefik.http.routers.myapp.entrypoints: websecure traefik.http.routers.myapp.tls.certresolver: letsencrypt
Health Checks
healthcheck: path: /up port: 3000 interval: 10s max_attempts: 30
Common Commands
First Deployment
# Bootstrap server (installs Docker, creates directories) kamal server bootstrap # Full setup (push config, start traefik, deploy app) kamal setup
Regular Deployment
# Deploy latest kamal deploy # Deploy specific version kamal deploy --version=abc123 # Deploy to staging kamal deploy -d staging
Rollback
# List available versions kamal app containers # Rollback to previous kamal rollback
Debugging
# SSH into container kamal app exec --interactive bash # View logs kamal app logs -f # Rails console kamal app exec --interactive "bin/rails console"
Accessories
# Start all accessories kamal accessory boot all # Restart specific accessory kamal accessory reboot db # Exec into accessory kamal accessory exec db --interactive psql -U postgres
Provisioning Workflow
Terraform + Ansible + Kamal Pipeline
# infra/bin/provision #!/usr/bin/env bash set -euo pipefail # 1. Terraform: Create infrastructure cd infra && tofu apply # 2. Ansible: Configure server SERVER_IP=$(tofu output -raw server_ip) cd ansible echo "[web]\n$SERVER_IP ansible_user=root" > hosts.ini ansible-playbook -i hosts.ini playbook.yml # 3. Kamal: Bootstrap containers cd ../.. bundle exec kamal server bootstrap
What Ansible Should Configure
Based on kamal-ansible-manager:
| Task | Purpose |
|---|---|
| Install Docker | Container runtime |
| Configure fail2ban | SSH intrusion prevention |
| Setup UFW | Firewall (22, 80, 443) |
| Enable NTP | Time synchronization |
| Create swap | Memory overflow protection |
| Harden SSH | Disable password auth, root login |
| Kernel tuning | swappiness, somaxconn |
Builder Configuration
Native ARM64 Builds (Hetzner CAX)
builder: arch: arm64 # OR for multi-arch: # multiarch: true
Remote Builder
builder: remote: arch: amd64 host: ssh://builder@build-server
Hooks
Pre-Deploy
# .kamal/hooks/pre-deploy #!/bin/sh echo "Running pre-deploy tasks..." bundle exec rails assets:precompile
Post-Deploy
# .kamal/hooks/post-deploy #!/bin/sh echo "Running migrations..." kamal app exec "bin/rails db:migrate"
Directory Structure
myapp/ ├── config/ │ └── deploy.yml # Main Kamal config ├── .kamal/ │ ├── secrets # Secret values (git-ignored) │ ├── secrets.staging # Staging secrets (git-ignored) │ └── hooks/ │ ├── pre-deploy │ └── post-deploy └── Dockerfile # Application container
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Connection refused | Docker not running | |
| Permission denied | SSH key not authorized | Check server's authorized_keys |
| Health check failing | App not starting | Check |
| Registry auth failed | Wrong credentials | Verify |
| Traefik 502 | Container not healthy | Increase |