mcpField
K
kamal-coder

kamal-coder

github.com/majesticlabs-devCloud & DevOps5057 installsUpdated 7w ago
Repository

About kamal-coder

kamal-coder is a skill for configuring and operating Kamal 2 deployments, aimed at teams deploying containerized applications to servers. It covers deployment settings, proxy and secrets configuration, multi-role services, and routine operations.

Key features

  1. 01Kamal 2 deployment configuration
  2. 02Web and job worker roles
  3. 03Proxy, SSL, and health checks
  4. 04Secrets and environment overrides
  5. 05Deploy, rollback, and debugging commands
  6. 06Builder and accessory setup

Use cases

  1. 01Set up production or staging deployments
  2. 02Run web and job containers on servers
  3. 03Configure SSL and health checks
  4. 04Inspect logs or roll back a release

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill kamal-coder

After installing, type/kamal-coderin Claude Code to confirm it loaded.

Bundled filesreferences/docker-patterns.mdreferences/request-flow.md

Details

From the project README

Kamal 2 Coder

Servers need Docker, SSH access, and ports 22/80/443 open. Provision with Ansible or cloud-init.

Configuration: config/deploy.yml

Minimal Setup
service: myapp
image: myapp

servers:
  web:
    - 203.0.113.10

proxy:
  ssl: true
  host: myapp.com

registry:
  username: username
  password:
    - KAMAL_REGISTRY_PASSWORD

env:
  clear:
    RAILS_ENV: production
    RAILS_LOG_TO_STDOUT: "true"
  secret:
    - RAILS_MASTER_KEY
Multi-Role Setup (Web + Job Worker)
service: myapp
image: myapp

servers:
  web:
    - 203.0.113.10
  job:
    hosts:
      - 203.0.113.10
    cmd: bin/jobs start

proxy:
  ssl: true
  host: myapp.com

registry:
  username: username
  password:
    - KAMAL_REGISTRY_PASSWORD

env:
  clear:
    RAILS_ENV: production
    SOLID_QUEUE_IN_PUMA: false
  secret:
    - RAILS_MASTER_KEY

Job worker notes:

  • cmd: bin/jobs start runs Solid Queue in a separate container
  • Set SOLID_QUEUE_IN_PUMA: false to disable in-process queue
  • Job role has no proxy — only web role serves HTTP traffic
With Local Registry

Eliminates Docker Hub dependency, rate limits, and external costs:

registry:
  server: localhost:5555
  username: ignored
  password:
    - KAMAL_REGISTRY_PASSWORD

Deploy the registry as an accessory:

accessories:
  registry:
    image: registry:2
    host: 203.0.113.10
    port: "5555:5000"
    volumes:
      - registry_data:/var/lib/registry
With Accessories
accessories:
  db:
    image: postgres:16
    host: 203.0.113.10
    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: 203.0.113.10
    port: 6379
    directories:
      - data:/data
    cmd: redis-server --appendonly yes
Docker Volumes for Persistence

For SQLite + ActiveStorage apps, mount a named volume:

servers:
  web:
    hosts:
      - 203.0.113.10
    volumes:
      - myapp_storage:/rails/storage
    labels:
      docker-volume-backup.stop-during-backup: "true"
  job:
    hosts:
      - 203.0.113.10
    cmd: bin/jobs start
    volumes:
      - myapp_storage:/rails/storage

Both web and job containers share the same volume for database access.

Proxy Configuration (kamal-proxy)

Kamal 2 uses kamal-proxy (not Traefik). It handles SSL termination, routing, and zero-downtime deploys.

Basic SSL
proxy:
  ssl: true
  host: myapp.com

Automatic Let's Encrypt certificate provisioning — no manual cert management.

Custom Port
proxy:
  ssl: true
  host: myapp.com
  app_port: 3000
Multiple Hosts
proxy:
  ssl: true
  hosts:
    - myapp.com
    - www.myapp.com
Health Check
proxy:
  ssl: true
  host: myapp.com
  healthcheck:
    path: /up
    interval: 3
    timeout: 3
Response Timeout
proxy:
  ssl: true
  host: myapp.com
  response_timeout: 30

Secrets: .kamal/secrets

Kamal reads secrets from .kamal/secrets (git-ignored).

With 1Password CLI
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")
With Environment Variables
KAMAL_REGISTRY_PASSWORD=$DOCKERHUB_TOKEN
RAILS_MASTER_KEY=$RAILS_MASTER_KEY
DATABASE_URL=$DATABASE_URL
Multi-Environment
# config/deploy.yml — base config
service: myapp

# config/deploy.staging.yml — overrides
service: myapp-staging
servers:
  web:
    - 203.0.113.20
proxy:
  host: staging.myapp.com
# .kamal/secrets.staging
RAILS_MASTER_KEY=$(op read "op://MyApp/staging/master_key")

Deploy with: kamal deploy -d staging

Common Commands

First Deployment
# One-time: installs kamal-proxy, pushes image, deploys
kamal setup

# Subsequent: builds, pushes, rolling restart
kamal deploy
Regular Operations
kamal deploy                    # Deploy latest
kamal deploy --version=abc123   # Deploy specific version
kamal deploy -d staging         # Deploy to staging
kamal redeploy                  # Redeploy without building
Rollback
kamal app containers            # List available versions
kamal rollback <version>        # Rollback to specific version
Debugging
kamal app exec --interactive bash              # Shell into container
kamal app logs -f                              # Tail logs
kamal app exec --interactive "bin/rails console"  # Rails console
kamal app exec "bin/rails db:migrate"          # Run migrations
Accessories
kamal accessory boot all        # Start all accessories
kamal accessory reboot db       # Restart specific accessory
kamal accessory exec db --interactive "psql -U postgres"
kamal accessory logs litestream  # View accessory logs

Builder Configuration

Native Builds
builder:
  arch: amd64
Multi-Architecture
builder:
  multiarch: true
Remote Builder
builder:
  remote:
    arch: amd64
    host: ssh://builder@build-server
Build Arguments
builder:
  args:
    RUBY_VERSION: "3.3.0"

Hooks

Pre-Deploy
# .kamal/hooks/pre-deploy
#!/bin/sh
echo "Running pre-deploy checks..."
Post-Deploy
# .kamal/hooks/post-deploy
#!/bin/sh
echo "Deploy complete: $(date)"
curl -s https://notify.example.com/deploy

Provisioning Workflow

Ansible + Kamal Pipeline
# 1. Ansible: Configure server
ansible-playbook -i hosts.ini playbook.yml

# 2. Kamal: Bootstrap and deploy
kamal setup
What Ansible Should Configure

Based on kamal-ansible-manager:

TaskPurpose
Install DockerContainer runtime
Configure fail2banSSH intrusion prevention
Setup UFWFirewall (22, 80, 443)
Enable NTPTime synchronization
Create swapMemory overflow protection
Harden SSHDisable password auth, root login
Unattended upgradesAutomatic security patches

Litestream Backup Accessory

For SQLite apps, add Litestream as an accessory (see litestream-coder for full config):

accessories:
  litestream:
    image: litestream/litestream:0.3
    host: 203.0.113.10
    cmd: replicate
    volumes:
      - myapp_storage:/rails/storage:ro
    files:
      - config/litestream.yml:/etc/litestream.yml
    env:
      secret:
        - LITESTREAM_ACCESS_KEY_ID
        - LITESTREAM_SECRET_ACCESS_KEY

Mount storage as read-only (:ro) — Litestream only reads WAL files.

Directory Structure

myapp/
├── config/
│   ├── deploy.yml           # Main Kamal config
│   └── deploy.staging.yml   # Staging overrides
├── .kamal/
│   ├── secrets              # Production secrets (git-ignored)
│   ├── secrets.staging      # Staging secrets (git-ignored)
│   └── hooks/
│       ├── pre-deploy
│       └── post-deploy
├── Dockerfile               # Application container
└── docker-entrypoint.sh     # Entrypoint script

Troubleshooting

IssueCauseFix
Connection refusedDocker not runningkamal setup or check Docker service
Permission deniedSSH key not authorizedCheck server's authorized_keys
Health check failingApp not startingCheck kamal app logs
Registry auth failedWrong credentialsVerify .kamal/secrets
502 Bad GatewayContainer not healthyIncrease healthcheck timeout
SSL cert not issuedDNS not pointing to serverVerify DNS A record
Asset 404 after deployVolume not mountedCheck volumes: in deploy.yml

References

Comments

Markdown supported