Harbor

AI-first deployment manager for KAMAL and Coolify. Manage multiple Rails apps across VPS instances from one CLI, and give LLM agents (Claude Code, Cursor, etc.) full deploy/rollback/logs/exec access to your fleet via MCP.

Harbor is backend-agnostic. Use KAMAL for lightweight deploys with no platform dependency, Coolify for a managed dashboard experience, or mix both in the same fleet. Same tools, same slash commands, different engines.

Why

You have 4-10 Rails apps deployed across multiple servers. Today, managing them means context-switching between project directories, remembering which server runs what, and manually relaying information to your AI coding assistant.

Harbor fixes this:

harbor logs myapp
harbor deploy api
harbor rollback admin abc123

And more importantly, your LLM agent can do it too:

You: "deploy the api and check if it's healthy"
Agent: harbor_deploy(project: "api")
Agent: harbor_get_logs(project: "api", lines: 20, grep: "ERROR")
Agent: "Deployed successfully. No errors in the last 20 log lines."

Install

gem install harbor-deploy

Requires Ruby >= 3.1. For the KAMAL backend, you also need KAMAL >= 2.11. The Coolify backend only needs an API token.

Quickstart

1. Register your projects

# KAMAL-deployed projects
harbor add myapp ~/src/myapp
harbor add api ~/src/api-service

# Or auto-detect KAMAL projects in common directories
harbor setup

harbor add validates that the project has a config/deploy.yml and auto-detects destinations (staging, production) from config/deploy.*.yml files.

2. Verify

harbor list
Projects:
  myapp (production, staging) - Main customer app
    /home/cole/src/myapp
  api (production)
    /home/cole/src/api-service

3. Configure MCP for your AI agent

Add to your Claude Code settings (.claude/settings.json):

{
  "mcpServers": {
    "harbor": {
      "command": "harbor",
      "args": ["mcp"]
    }
  }
}

For Cursor, add to .cursor/mcp.json. Any MCP-compatible client works.

4. Use it

From the CLI:

harbor status myapp
harbor deploy myapp -d production
harbor logs api -n 50 --grep ERROR
harbor runner myapp "puts User.count"

From your LLM agent (it calls these automatically):

"deploy myapp to production"
"check the api logs for errors"
"how many users signed up today?"
"roll back admin to the previous version"

Backends

Harbor supports two deployment backends. Configure per-project in ~/.harbor/config.yml.

KAMAL (default)

Deploys via SSH using Docker containers and kamal-proxy. No platform dependency, no external service. Uses local Docker registry (no Docker Hub needed).

projects:
  myapp:
    path: ~/src/myapp
    backend: kamal   # default, can omit

Coolify

Deploys via the Coolify API. Works with Coolify Cloud or self-hosted instances. Get your API token from Coolify dashboard under Keys & Tokens.

projects:
  cloud-app:
    path: ~/src/cloud-app
    backend: coolify
    coolify:
      coolify_url: https://app.coolify.io
      coolify_token: "your-api-token"
      coolify_app_uuid: "your-app-uuid"

Find your app UUID in Coolify dashboard URL or via:

curl -s https://app.coolify.io/api/v1/applications \
  -H "Authorization: Bearer YOUR_TOKEN" | python3 -m json.tool

Mixed fleet

Run both backends in the same fleet. Same MCP tools, same slash commands:

projects:
  fleet-app:
    path: ~/src/fleet
    backend: kamal
    description: "KAMAL deploy, local registry"
  cloud-app:
    path: ~/src/cloud
    backend: coolify
    coolify:
      coolify_url: https://app.coolify.io
      coolify_token: "your-token"
      coolify_app_uuid: "app-uuid"
    description: "Coolify managed"
harbor deploy fleet-app    # → kamal deploy (SSH + Docker)
harbor deploy cloud-app    # → Coolify API deploy
harbor logs fleet-app      # → kamal app logs
harbor logs cloud-app      # → Coolify API /applications/{uuid}/logs

Your team doesn't need to know which backend is running. They just type /deploy.

CLI Reference

Project Management

harbor add NAME PATH              # Register a KAMAL project
harbor add myapp ~/src/myapp -d production,staging

harbor remove NAME                # Unregister a project

harbor list                       # List all registered projects

harbor setup                      # Interactive quickstart: auto-detect projects,
                                  # validate config, output MCP settings

Deploy & Rollback

harbor deploy PROJECT             # Deploy (uses default destination)
harbor deploy myapp -d staging    # Deploy to specific destination
harbor deploy myapp --skip-push   # Deploy without rebuilding image

harbor rollback PROJECT VERSION   # Rollback to a git SHA
harbor rollback myapp abc1234 -d production

Logs

harbor logs PROJECT               # Last 100 lines
harbor logs myapp -n 500          # Last 500 lines
harbor logs myapp --grep ERROR    # Filter by pattern
harbor logs myapp -r workers      # Specific role
harbor logs myapp -f              # Follow (stream live, KAMAL only)

Execute Commands

harbor exec PROJECT "CMD"         # Run a command in the container
harbor exec myapp "cat log/production.log | tail -50"
harbor exec api "printenv" -r workers

Rails Commands

harbor rails PROJECT console      # Interactive rails console (TTY, KAMAL only)
harbor rails PROJECT dbconsole    # Interactive database console (TTY, KAMAL only)
harbor rails PROJECT migrate      # Run migrations
harbor rails PROJECT migrate:status
harbor rails PROJECT routes       # Show routes
harbor rails PROJECT routes users # Grep routes
harbor rails PROJECT seed         # Seed database
harbor rails PROJECT rollback     # Rollback last migration

harbor runner PROJECT "SCRIPT"    # Run Ruby code via rails runner
harbor runner myapp "puts User.count"
harbor runner myapp "pp Order.last(5).pluck(:id, :status)"

Status & Details

harbor status                     # Status of all projects
harbor status myapp               # Status of one project
harbor details myapp              # Full container + proxy details
harbor audit                      # Deployment history (all projects)
harbor audit myapp -n 50          # Last 50 entries for one project

Maintenance Mode

harbor maintenance myapp on       # Enable (stops routing / stops app)
harbor maintenance myapp off      # Disable (resume traffic)

MCP Server

harbor mcp                        # Start MCP server (stdio, for AI agents)

MCP Tools

When connected via MCP, your AI agent has access to 19 tools:

Tool What it does Safety
harbor_add_project Register a project with Harbor write
harbor_remove_project Unregister a project write
harbor_setup_kamal Initialize KAMAL config in a project write
harbor_setup_autodeploy Set up deploy-on-merge (webhook on server) write
harbor_list_projects List all registered projects read-only
harbor_project_status Container status for a project read-only
harbor_deploy Deploy a project write, audited, locked
harbor_rollback Rollback to a specific version write, audited, locked
harbor_get_logs Get recent app logs read-only
harbor_exec Execute a command in the container write, audited
harbor_runner Execute Ruby code via rails runner write, audited
harbor_rails Run Rails commands (migrate, routes, seed, etc.) write, audited
harbor_details Container and proxy details read-only
harbor_audit View deployment history read-only
harbor_list_servers List servers across projects read-only
harbor_maintenance Toggle maintenance mode write, audited
harbor_app_version Get current deployed version read-only
harbor_ci Run local CI suite (bin/ci) read-only
harbor_ship Full pipeline: CI, review, PR, merge, deploy write, audited

All tools work with both KAMAL and Coolify backends.

Ship Pipeline

The harbor_ship tool (or /ship slash command) runs the full pipeline:

Step 1: Local CI        → bin/ci (tests, rubocop, brakeman, bundler-audit)
Step 2: Local review    → CodeRabbit CLI or Codex (seconds, no push needed)
Step 3: Push + create PR
Step 4: Async review    → CodeRabbit cloud / teammates approve
Step 5: Merge PR
Step 6: Deploy          → KAMAL or Coolify (based on backend config)
Step 7: Verify          → check logs for errors

By default, deploy_on_merge is true. Merge to main triggers deployment automatically via GitHub Actions (KAMAL) or Coolify's built-in auto-deploy.

Auto-Deploy on Merge

With Coolify

Coolify handles this natively. Enable auto-deploy in your Coolify app settings or via API. No extra setup needed.

With KAMAL

Two options:

Option A: GitHub Actions (add 3 secrets to your repo)

Harbor generates a .github/workflows/deploy.yml that runs kamal deploy on push to main. Requires SSH_PRIVATE_KEY, DEPLOY_HOST, and RAILS_MASTER_KEY as repo secrets.

Option B: Server webhook (zero secrets, Coolify-style)

harbor_setup_autodeploy(project: "myapp")

One MCP call. SSHes into your server, clones the repo, installs a webhook service, and configures the GitHub webhook. The server deploys to itself on push to main. No GitHub secrets needed.

Configuration

Harbor stores its config at ~/.harbor/config.yml:

version: 1
projects:
  # KAMAL backend
  myapp:
    path: /home/cole/src/myapp
    destinations:
      - production
      - staging
    default_destination: production
    description: "Main customer-facing app"
    allowed_operations:                    # optional, omit to allow all
      - deploy
      - rollback
      - logs
      - exec
    allowed_exec_commands:                 # optional, omit to allow all
      - "rails runner *"
      - "rails db:migrate"
      - "cat log/*"

  # Coolify backend
  cloud-app:
    path: /home/cole/src/cloud-app
    backend: coolify
    coolify:
      coolify_url: https://app.coolify.io
      coolify_token: "your-api-token"
      coolify_app_uuid: "your-app-uuid"

settings:
  lock_timeout: 1800        # 30 min, seconds before deploy lock auto-expires
  deploy_timeout: 600       # 10 min, max seconds for a deploy operation
  exec_timeout: 120         # 2 min, max seconds for exec/runner
  logs_timeout: 30          # 30 sec, max seconds for log retrieval
  audit_db: ~/.harbor/audit.db
  lock_dir: ~/.harbor/locks

Operation Allowlists

Restrict what an AI agent can do per project:

projects:
  production-app:
    path: /home/cole/src/prod
    allowed_operations:
      - logs
      - status
      - details
      - audit
    # No deploy, rollback, exec, or maintenance — read-only for this project

Exec Command Safety

Commands are parsed into argv arrays. Shell metacharacters are always rejected:

; | & ` $ ( ) { } > < \n \

This prevents injection regardless of allowlist configuration. When allowed_exec_commands is set, only matching commands are permitted.

Safety

Harbor is designed for AI agents deploying to production. Safety is not optional.

Audit Log

Every write operation (deploy, rollback, exec, runner, maintenance) is logged in a SQLite database at ~/.harbor/audit.db:

harbor audit
Timestamp            Project      Operation    Dest       Status   Duration
-------------------- ------------ ------------ ---------- -------- --------
2026-04-06T02:30:00  myapp        deploy       production success  45200ms
2026-04-06T02:28:00  api          runner       production success  1200ms
2026-04-06T02:25:00  myapp        rollback     production success  12000ms

Each entry records whether it was triggered by cli or mcp, so you always know if a human or an agent took the action.

Deploy Locks

Concurrent deploys to the same project and destination are prevented. The lock includes:

  • PID of the process holding the lock
  • Timestamp (auto-expires after 30 minutes)
  • Initiator (cli or mcp)
  • Stale lock detection (checks if PID is still alive)

Shell Injection Prevention

The exec and runner tools reject all shell metacharacters at the parsing layer. Commands are split into argv arrays, never passed through a shell interpreter. This applies to both CLI and MCP usage.

Architecture

┌─────────────────────────────────────────────────────────┐
│                    harbor (gem)                           │
│                                                           │
│  ┌──────────┐  ┌───────────────┐  ┌───────────────────┐ │
│  │   CLI    │  │  MCP Server   │  │    Audit Log      │ │
│  │  (Thor)  │  │  (stdio)      │  │    (SQLite)       │ │
│  └────┬─────┘  └───────┬───────┘  └────────┬──────────┘ │
│       │                │                     │            │
│       └────────┬───────┘                     │            │
│                │                              │            │
│        ┌───────▼──────────┐                  │            │
│        │ project.adapter  ├──────────────────┘            │
│        └──┬──────────┬────┘                               │
│           │          │                                     │
│   ┌───────▼──────┐ ┌▼──────────────┐                     │
│   │ KamalAdapter │ │ CoolifyAdapter│                     │
│   │              │ │               │                     │
│   │ SSH + Docker │ │ REST API      │                     │
│   │ subprocess   │ │ HTTPS         │                     │
│   └──────┬───────┘ └───────┬───────┘                     │
│          │                 │                              │
└──────────┼─────────────────┼──────────────────────────────┘
           │                 │
   ┌───────▼──────┐  ┌──────▼───────┐
   │ Remote Hosts │  │ Coolify API  │
   │ (SSH+Docker) │  │ (Cloud/Self) │
   └──────────────┘  └──────────────┘

How the adapters work

KAMAL backend:

  • Read operations (status, logs, details): KAMAL Ruby API for structured data
  • Write operations (deploy, rollback): kamal CLI subprocess (battle-tested orchestration)
  • Exec/runner: kamal app exec subprocess

Coolify backend:

  • All operations go through the Coolify REST API
  • Deploy: GET /api/v1/deploy?uuid=X
  • Logs: GET /api/v1/applications/{uuid}/logs
  • Exec: POST /api/v1/servers/{uuid}/command (via docker exec)
  • Status: GET /api/v1/applications/{uuid}

Slash Commands (Claude Code Skills)

Add these skills to your project's .claude/skills/ directory for zero-friction team adoption:

Command What it does
/harbor-ship Full pipeline: CI, review, PR, merge, deploy, verify
/harbor-ci Run local CI suite (bin/ci)
/harbor-deploy Deploy current version to production
/harbor-logs Check production logs
/harbor-status Check what's running in production
/harbor-setup First-time setup for new teammates

New teammate onboarding

# 1. Clone the repo (skills come with it)
git clone <repo> && cd myapp

# 2. Install Harbor
gem install harbor-deploy

# 3. Open Claude Code, type /harbor-setup
#    It configures everything automatically

Development

git clone https://github.com/cole-robertson/harbor
cd harbor
bundle install
bundle exec rspec          # 136 tests

Project Structure

lib/harbor/
├── version.rb             # Version constant
├── config.rb              # ~/.harbor/config.yml management
├── project.rb             # Project model + backend selection + exec safety
├── kamal_adapter.rb       # KAMAL CLI wrapper with ENV/Dir safety
├── coolify_adapter.rb     # Coolify REST API adapter
├── audit_log.rb           # SQLite WAL audit trail
├── deploy_lock.rb         # File-based deploy locking
├── cli.rb                 # Thor CLI entry point
├── cli/
│   ├── projects.rb        # add, remove, list, setup
│   ├── deploy.rb          # deploy, rollback, exec, maintenance
│   ├── logs.rb            # logs with follow support
│   ├── rails.rb           # Rails commands + interactive console
│   ├── servers.rb         # status, details, audit
│   └── mcp_command.rb     # harbor mcp entry point
└── mcp/
    ├── server.rb          # JSON-RPC over stdio
    └── tools.rb           # 19 MCP tool definitions + handlers

Not Supported (v1)

  • KAMAL accessories (Redis, Postgres) management
  • Web dashboard or UI
  • Multi-user RBAC / policy engine
  • Notification hooks (Slack, webhook)
  • Auto-rollback on health check failure
  • Project auto-discovery scanning

These are tracked for v2 based on real usage.

License

MIT