AppCrane
AppCrane
The self-hosted home for the apps your AI builds and your AI deploys.
Vibe-code an app with Claude Code or Cursor, then have your AI agent deploy it — over MCP — to a server you own. AppCrane is a self-hosted, agent-first deployment platform with the enterprise guardrails the cloud PaaS crowd skips: Docker isolation per app, SAML/OIDC/SCIM SSO, per-user audit, and a middleware hard-wall so the platform operator can't read your app secrets (your model API keys stay yours). A self-hosted alternative to Heroku, Vercel, and hosted agent-deploy services like AppDeploy.
MCP-first. AI agents connect once via claude mcp add ... /api/mcp and operate the platform through 39 appcrane_* tools. No curl, no separate scripts — appcrane_get_guide(topic="onboarding"|"operations") returns the latest playbook on demand.
Why AppCrane
Feature | AppCrane | Coolify | Dokploy |
Agent-first / MCP-native | ✅ | ❌ | ~ add-on |
Self-hosted, your infra | ✅ | ✅ | ✅ |
Enterprise SSO (SAML/OIDC/SCIM) | ✅ | ~ | ❌ |
Secret hard-wall (operator can't read) | ✅ | ❌ | ❌ |
Managed repo (no GitHub account) | ✅ | ❌ | ❌ |
Docker isolation per app | ✅ | ✅ | ✅ |
Dual sandbox/prod environments | ✅ | ~ | ✅ |
Zero-downtime deploys | ✅ | ~ | ✅ |
Open source | ✅ AGPL-3.0 | ✅ Apache-2.0 | ✅ Apache-2.0 |
Full matrix vs AWS Copilot / App Runner / Lightsail / CodeDeploy / Vercel / AppDeploy → glick.run/comparison.html
Related MCP server: Nexlayer MCP
Features
Docker container isolation — every app runs in its own container; no shared dependencies, no runaway processes
Enterprise SSO — SAML 2.0, OIDC, and SCIM provisioning; connect to Okta, Azure AD, Google Workspace
Identity forwarded to apps as headers —
X-AppCrane-User-Role,X-AppCrane-App-Role, etc. are injected by the proxy afterforward_authverifies the user; deployed apps read identity directly off the request without a callback (oauth2-proxy / IAP pattern)/api/meendpoint — canonical "who is the caller" for proxied apps; accepts thecc_tokencookie, Bearer, orX-API-Key; returns global role + per-app role (?app=<slug>orReferer-inferred)Headless app type — set
auth_mode: 'headless'to bypassforward_authentirely on an app; right tool for telemetry ingest, public webhooks, status pages, and single-purpose unauthenticated servicesAppStudio AI pipeline — AI proposes code improvements on a schedule; you review and approve before anything ships
Real-time presence — see who's active on each app, which environment, and when they last deployed
Dual environments per app: production + sandbox, always-on, separate ports
Auto-HTTPS via Caddy reverse proxy with Let's Encrypt
GitHub webhook auto-deploy on push (HMAC-verified)
Zero-downtime deploys (start new, health check, swap, drain old)
Rollback in seconds (symlink-based, keeps last 5 releases)
Encrypted env vars (AES-256-GCM) — admin cannot read them by design
Health checks with auto-restart and email notifications
Audit log for every action
MCP server at
/api/mcpexposing 39appcrane_*tools — agents operate the platform without ever touching curl, gh, or shell
Quick Start
One command on a fresh Ubuntu server installs and wires up everything — Node, Caddy (with automatic HTTPS), Docker, the systemd service, an encrypted-secrets key, and your admin user:
curl -fsSL https://raw.githubusercontent.com/gitayg/appCrane/main/install.sh | sudo bashIt prompts for just two things — your domain and admin email — and is safe to re-run. When it finishes, point your domain's DNS at the server and you're live.
Prerequisites: a fresh Ubuntu server (root / sudo) and a domain whose DNS A
record points at it — Caddy provisions TLS automatically on first request.
Non-interactive (CI / automation) — no prompts:
sudo CRANE_DOMAIN=crane.example.com ADMIN_EMAIL=admin@example.com bash install.sh
# flags also work: --domain / --admin-email / --admin-name / --tls-cert / --tls-keyEverything below is done for you, idempotently, by the one command above:
Node.js 20 + AppCrane, with the
craneCLI linked globallyCaddy — the reverse proxy that routes
<domain>/<slug>to each app, runs the SSO auth, injects theX-AppCrane-*identity headers, and auto-provisions TLS — plus the group, file permissions, and asudoersrule so AppCrane can reload Caddy on every deployDocker + a systemd
appcraneservice (Restart=always— survives crashes and reboots, and powers one-click self-update)A
.envwith a freshly generatedENCRYPTION_KEY— back this up; losing it makes every stored secret unrecoverable — and your admin user (crane init)
Installing by hand means reproducing all of that — especially the Caddy install +
permissions + sudoers, which is the most-missed step and later surfaces as
permission errors or apps that never receive their identity headers. If you must,
treat install.sh as the source of truth rather than a shortened list.
AppStudio (optional): to enable AI app-building, set an Anthropic API key —
systemctl edit appcrane --force, addEnvironment="ANTHROPIC_API_KEY=sk-ant-..."under[Service], thensystemctl daemon-reload && systemctl restart appcrane.
Deploy your first app
The installer already created your admin user, so once DNS points at the box:
# Reachable at https://<your-domain>/myapp
crane app create --name "MyApp" --slug myapp --repo https://github.com/yourorg/myapp
crane deploy myapp --env sandbox
# Give a teammate access (optional)
crane user create --name sarah --email sarah@example.com
crane app assign myapp --email sarah@example.comCLI Reference
Server
crane status # Server health: CPU, RAM, disk, apps
crane config --show # Show CLI config
crane config --url http://localhost:5001 # Set API URL
crane config --key dhk_admin_xxx # Set API key
# Recover a lost platform-owner API key (run on the box, direct DB).
# Defaults to the platform_admin; override to target a specific account:
crane regenerate-key # Regenerate the platform owner's key
crane regenerate-key --email you@ex.com # ...for a specific user by email
crane regenerate-key --user-id 1 # ...or by user idMigrate config between instances
Move the platform settings (including encrypted secrets) to another AppCrane —
without sharing encryption keys. Export keeps secrets ciphertext; import
re-encrypts them with the target instance's own key.
# On the SOURCE instance:
crane config export --out config.json
# Copy config.json to the TARGET, then on the TARGET:
OLD_ENCRYPTION_KEY=<source ENCRYPTION_KEY> crane config import config.jsonThe source ENCRYPTION_KEY (from the source's .env) is needed only to decrypt
the secrets during import; it is used transiently, never stored. One-way values
(e.g. the SCIM token, stored as a hash) can't be migrated — the import lists them
to regenerate on the target. Delete config.json afterward.
Apps (admin)
crane app list
crane app create --name X --slug x --domain x.example.com --repo https://github.com/...
crane app info myapp
crane app delete myapp --confirm
crane app assign myapp --email user@example.comDeploy (app user)
crane deploy myapp --env sandbox
crane deploy myapp --env production
crane deploy:history myapp --env prod
crane deploy:log myapp --id 5
crane rollback myapp --env production
crane promote myapp # sandbox → production, zero downtimeEnv Vars (app user — admin cannot access)
crane env set myapp --env sandbox DATABASE_URL=postgres://... API_KEY=sk-test
crane env list myapp --env production
crane env list myapp --env sandbox --reveal
crane env delete myapp API_KEY --env sandboxHealth, Webhooks, Backups
crane health status myapp
crane health config myapp --env prod --endpoint /api/health --interval 30
crane webhook myapp --auto-sandbox on
crane backup create myapp --env prod
crane backup list myapp
crane logs myapp --env production
crane audit --app myappMCP (for AI agents)
AppCrane is MCP-first. One claude mcp add and the agent gets 35
appcrane_* tools — list apps, deploy, set/get secrets, read logs,
manage access, rotate icons, the lot. Tool names are AWS-aligned
(stage, set_secret/get_secret, cp).
claude mcp add --transport http appcrane https://crane.example.com/api/mcp \
--header "X-API-Key: dhk_admin_or_user_xxxxxxxxxxxxx" \
--header "X-Github-Token: ghp_your_github_pat"Then in any Claude Code session:
Onboard a new app. Start by calling
appcrane_get_guidewithtopic="onboarding"for the playbook.
The agent pulls the current guide from the server, so edits propagate
without a redeploy of your tooling. topic="operations" returns the
post-onboarding reference (deploy lifecycle, troubleshooting fast
failures, access management, etc.).
Architecture
Ubuntu Server
├── Caddy (reverse proxy, auto-HTTPS)
│ ├── myapp.example.com → production app
│ └── myapp-sandbox.example.com → sandbox app
├── Docker (container isolation)
│ ├── myapp-production ← isolated container per env
│ └── myapp-sandbox
├── AppCrane API (:5001)
│ ├── Express 5 + SQLite
│ ├── Health checker (cron)
│ ├── SSO (SAML / OIDC / SCIM)
│ ├── AppStudio AI pipeline
│ └── Presence (WebSocket)
└── /data/apps/myapp/
├── production/releases/ (symlink-based, last 5)
└── sandbox/releases/Security
Init locked to localhost — admin setup only from the server itself
API key auth — all requests require
X-API-KeyheaderAdmin isolation — admin cannot read env vars or
/data/; enforced at middleware levelAES-256-GCM encrypted env vars at rest
Webhook HMAC verification for GitHub
SCIM deprovisioning — removing a user from your IdP revokes AppCrane access automatically
All actions audited — who did what, when
Identity contract for deployed apps
Apps deployed on AppCrane never need to implement their own auth. The Caddy proxy verifies every request against /api/identity/verify before forwarding it to the container, and the result is delivered to the app in three complementary ways. Apps should consume them in this precedence order:
1. Request headers (zero-fetch, recommended)
Caddy copy_headers the verified identity onto the upstream proxy request. The app reads them directly:
Header | Value | Notes |
| Backward-compat single identifier. Always set for authenticated requests. | |
| numeric id (string) | Always set. |
| Granular. May be absent if the user has no email. | |
| display name, |
|
|
| Raw token, underscore intact. Always set. |
|
| Per-app role. Platform admins collapse to |
Trust model: the Caddy generator emits request_header -X-AppCrane-* strip directives before the forward_auth block in each per-app handler. Caddy zeroes out any client-set X-AppCrane-* headers first, then copy_headers re-injects only what /verify returned. Header smuggling is impossible — what the app receives is guaranteed platform-issued.
Absence semantics: if X-AppCrane-User-Role isn't on the request, the request was not verified (Caddy failed closed at forward_auth and you wouldn't receive it). So presence = trusted.
// Express example
app.use((req, res, next) => {
const role = req.get('X-AppCrane-User-Role') // 'platform_admin' | 'admin' | 'user'
const appRole = req.get('X-AppCrane-App-Role') // 'owner' | 'admin' | 'user' | 'viewer'
const email = req.get('X-AppCrane-User-Email') || req.get('X-AppCrane-User')
req.user = role ? { id: req.get('X-AppCrane-User-Id'), email, role, appRole } : null
next()
})2. GET /api/me (when you need more than the basics)
Returns the full user object — name, email, username, global role — plus the per-app role for whatever app the caller is asking about. Same origin as the app, so the browser auto-sends cc_token; no SDK or token plumbing required:
const r = await fetch('/api/me') // ?app=<slug> optional; Referer-inferred otherwise
if (r.status === 401) { location.href = '/login?redirect=' + encodeURIComponent(location.href); return }
const { user, app_role } = await r.json()Auth precedence inside /api/me:
cc_tokencookie (proxied apps' default —httpOnly, browser-managed).Authorization: Bearer <session>(CLI / programmatic).X-API-Key: dhk_*(admin / agent keys).
App slug resolution:
Explicit
?app=<slug>query.Referer-inferred (first path segment; sandbox-suffix retry).Lean global-only payload if neither resolves.
3. Headless apps — opt out entirely
For services where the whole app is meant to be unauthenticated — telemetry ingest, public webhooks, status pages, the squash CLI's ping/stats — set the app's auth_mode to headless (owner-only toggle in the Launcher, or appcrane_set_app_meta slug=<…> auth_mode=headless via MCP). The Caddy block then skips forward_auth, copy_headers, and the strips entirely. No X-AppCrane-* headers, no /api/me, no cc_token. The app's own server takes responsibility for any payload-level authn it needs (HMAC, install-id, IP allowlist, etc.).
Pick by shape:
The whole app is unauth ingest → headless app (clean separation, smaller blast radius).
Mostly-auth app with a couple of public endpoints → keep
authenticated, gate the public paths at the app's own router.
4. Per-tenant DB (multitenancy) — opt in
Opt in with "multitenant": true in deployhub.json and AppCrane gives each of
your app's users an isolated SQLite database on the persistent /data volume —
you don't build tenant isolation yourself. A tenant is (org, user), where
org is the user's email domain. This is fully opt-in: apps that don't set
the flag are completely unaffected.
When enabled, AppCrane injects APPCRANE_TENANT_ROOT=/data/tenants. Use the
appcrane-tenant helper to derive the tenant DB from the
identity headers above (section 1) — no path-building by hand:
import { tenantDb } from 'appcrane-tenant'
app.get('/api/notes', (req, res) => {
const db = tenantDb(req) // opens /data/tenants/<org>/u<userId>/db.sqlite
res.json({ notes: db.prepare('SELECT * FROM notes').all() })
})tenantDbPath(req) returns just the path if you use a different SQLite driver.
Each tenant also gets a storage/ dir (tenantStorageDir(req) / tenantFile(req, name))
for files. Set "tenant_quota_mb": <n> in deployhub.json to cap per-tenant
usage — AppCrane injects it and assertTenantQuota(req) throws once a tenant is
full (the quota covers DB + storage).
Always build tenant paths via the helper (never from raw user input) — the
identity headers are platform-signed and the org slug is sanitised against
traversal. When a user's access is revoked, AppCrane purges that tenant's dir
automatically. Consumer domains (e.g. gmail.com) share an org label, but
isolation is per-user, so data never mixes. The helper isn't on npm yet — copy
packages/tenant/index.js or depend on it by path;
see the multitenant-notes example.
Permission Model
Action | Admin | App User |
Create/delete apps | Yes | No |
Assign users | Yes | No |
Server health | Yes | No |
Deploy / rollback / promote | No | Yes (own apps) |
View/edit env vars | No | Yes (own apps) |
Configure health/webhooks | No | Yes (own apps) |
Backups | No | Yes (own apps) |
Tech Stack
Node.js 20, Express 5, SQLite, Docker, Caddy 2, SAML/OIDC/SCIM, AES-256-GCM, Commander.js, Ubuntu 22.04+
License
GNU AGPL v3. Free and open source — use, modify, and self-host. If you run a modified version as a network service, you must make your source available under the same license. Need to run private modifications as a service, or embed AppCrane in a proprietary product? A commercial license is available.
Feedback & Contributions
Open an issue: https://github.com/gitayg/appCrane/issues
Pull requests welcome — please read CONTRIBUTING.md first. It includes the short CLA that keeps AppCrane's dual-licensing (AGPL + commercial) possible.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceA hosted AI software engineer that writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions. Connect from any MCP client (Claude Code, Cursor, Windsurf, or your own agents) and delegate engineering tasks.Last updated67MIT

Nexlayer MCPofficial
Flicense-qualityFmaintenanceAgentic cloud platform with 45+ MCP tools. Deploy any containerized stack, debug live pods (shell, file editing, DB queries), manage custom domains & TLS, push to built-in container registry, scale pods, and manage GPU workloads. The infrastructure layer where AI agents ship software to production.Last updated8
mcpgateofficial
Flicense-qualityAmaintenanceSelf-hosted MCP gateway that connects Claude, ChatGPT, and other AI agents to 20+ enterprise tools (GitLab, Jira, Notion, Google Workspace, Slack, Grafana, …) with OAuth, audit logs, and zero data leaving your infrastructureLast updated- Alicense-qualityBmaintenanceGives AI coding agents (Claude Code, Cursor, etc.) unified, secure access to dev infrastructure (Vercel, GitHub, Supabase, Cloudflare, GCP) via a single MCP token.Last updatedMIT
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
Real-time chat hub for AI agents — Claude Code, Cursor, Cline, Codex over MCP or REST.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/gitayg/appCrane'
If you have feedback or need assistance with the MCP directory API, please join our Discord server