The app works on your machine. What stands between it and a URL is a list of things that are not your problem and became your problem anyway: a Dockerfile, TLS, a database, a login screen, somewhere for the logs to go, and a way back to the last version that worked. AppCrane takes that list. It runs on a server someone in your organisation already owns, and you drive it from the agent you are already working in.
AppCrane serves an MCP endpoint, so Claude Code, Cursor, Codex or any other MCP client can operate it directly — 59 appcrane_* tools covering apps, deploys, rollbacks, secrets, logs, access and scanning. Each connection sees the subset its role allows. One claude mcp add and the loop is: describe the change, let the agent deploy it to sandbox, look at it, promote.
claude mcp add --transport http appcrane https://crane.example.com/api/mcp \ --header "X-API-Key: dhk_..."
There is no separate CLI to learn and no curl to remember. The agent can also ask the server for the current playbook with appcrane_get_guide, so it reads the procedure rather than inferring it from a tool list. The full walkthrough is on Deploy from Claude and Cursor.
Every app has two environments, both always on, on separate ports. You deploy to sandbox, look at the thing, and promote. Promotion refuses unless sandbox is live and currently healthy — you do not ship a broken sandbox to production — and the promoted production release is health-checked with automatic revert. For a GitHub-backed app the promote rebuilds production from the exact sandbox commit; for a managed or uploaded app it copies the exact release you tested.
Rolling back one release restarts the container image that release was already built into, instead of rebuilding it from source. AppCrane keeps the previous per-commit image alongside the running one by default, precisely because the one rollback you reach for during an incident is the one that should not be waiting on docker build.
The honest edges: rolling back further than one release, or on an app configured to keep no older images, rebuilds that commit first. And a rollback is recorded as a new deployment with the superseded one marked rolled back, so the history reads forward rather than pretending the bad release never happened.
If your app ships a Dockerfile it is used as-is. If it does not, AppCrane generates one: node:*-alpine for a Node app, or php:8.3-apache when it finds a composer.json. Both generated images run non-root and honour the port AppCrane assigns. Nixpacks covers the rest.
One caveat stated plainly, because it will otherwise surprise a PHP developer: a generated PHP build is health-checked at its front controller and held to "answers 200" rather than to AppCrane's {status, version} contract. AppCrane generated the Dockerfile, not the application, and Laravel and Symfony do not serve a health endpoint.
Ask for one and AppCrane provisions it per app — PostgreSQL, MariaDB, MongoDB or Redis — and injects the credentials into the environment under whatever variable names your app already reads, before the container starts. Nothing to write down, nothing to paste into a secret manager, and no shared database where another app's mistake becomes your outage. Mongo runs as a single-node replica set, so change streams and transactions work rather than failing at the first session.
The login screen is the part of an internal app least worth writing and most likely to be wrong. AppCrane authenticates the visitor at the proxy and hands your container the result:
X-AppCrane-User-Email: someone@example.com X-AppCrane-User-Id: 42 X-AppCrane-App-Role: admin X-AppCrane-Auth-Mode: authenticated
Read them and treat the caller as signed in: no password store, no session table, no invite email, no forgotten-password flow. When you need more than the basics there is GET /api/me, which answers "who is the caller" with the global role and the per-app role. And when the whole service is meant to be open — a webhook receiver, a telemetry ingest, a status page — set the app's auth mode to headless and the proxy stops checking at all.
Your app can also define its own role names — approver, reviewer, whatever fits — and AppCrane will store who holds them and deliver them in a header without ever interpreting them. You keep the vocabulary; the platform keeps the account.
Every deploy recreates the container, so anything outside the app's declared volumes is gone. Rather than warning on every deploy — which just teaches everyone to click through — AppCrane inspects the running container and warns only when state is genuinely at risk, naming what survives and what does not. It looks two ways: paths the image declares as VOLUME that nothing mounts, and paths the app has actually written that no mount covers. The second is the one that catches real cases, because plenty of frameworks persist to a directory they never declare.
The dashboard asks for a second confirmation. The API and MCP require an explicit acknowledge_data_loss and fail closed without it — most deploys here come from an agent, and a warning only a human sees is a warning most deploys never get.
Caddy provisions certificates on first request. You do not configure TLS and you do not renew anything.
Recent container output on demand, filterable by substring, from the dashboard or from your agent — plus the deploy log for the build itself.
Declare a health endpoint; AppCrane probes it, restarts on failure, and emails when it does.
Declare a cron array in deployhub.json and AppCrane runs it inside your container, keeping the last exit code and the tail of its output.
Environment variables are AES-256-GCM encrypted at rest, and the person administering the box cannot read the ones for apps they are not assigned to.
A managed app gets a bare git repository on the AppCrane host itself. Push to it; a push can deploy on its own when auto-deploy is on for that environment.
If your app is going to be embedded in an internal portal rather than opened on its own, the sign-in usually breaks in the frame. AppCrane handles that case: an app embedded by a site on the platform's own domain, or by an origin the app lists, can sign its user in without leaving the frame, and the deep link survives so a signed-out visitor lands on the app they asked for rather than on a dashboard. Password sign-in completes in the frame. OIDC and SAML hand off to the identity provider's own page — which many providers refuse to render in a frame — so there the sign-in page shows a button that opens a small top-level popup and closes it once you are through.
deployhub.json and synced at deploy time. There is no REST route to add one on the fly.No. A Node app is built from node:*-alpine and a PHP app with a composer.json from php:8.3-apache, both non-root and on the port AppCrane assigns; Nixpacks covers the rest. Ship a Dockerfile and it is used as-is.
Not for one release back — that restarts the image already built for it, because the previous per-commit image is kept by default. Further back than one release, or on an app keeping no older images, it rebuilds that commit first.
No. Read the X-AppCrane-* headers, or call GET /api/me. For a service meant to be open, set the app to headless and the proxy stops checking.
AppCrane inspects the running container, warns only when state is genuinely at risk, and names what survives. The dashboard asks twice; the API and MCP require an explicit acknowledge_data_loss and fail closed without it.
Yes. A managed app gets a bare git repository on the AppCrane host under DATA_DIR/repos, and deploys clone from it. No GitHub account involved.
Ask for one — PostgreSQL, MariaDB, MongoDB or Redis, provisioned per app, with credentials injected under the variable names your app already reads before the container starts.