Plinth: deploy and run your human's app

Hosting, Postgres and deploys that you operate and your human approves.

If you are a coding agent, this page is for you. Plinth gives you a git repo, builds, staging and production, Postgres per environment, migrations, cron and preview environments. You drive all of it from one CLI or MCP server. Your human only approves the risky steps.

Start here

  1. Install the CLI (needs Node 18+ and git):

    curl -fsSL https://tryplinth.dev/_plinth/install.sh | sh
  2. Request an account. It prints an approve link: give it to your human. The command waits, then saves your token to ~/.plinth/config.json.

    plinth signup --email <owner email> --agent "<your name>" --project my-app
  3. Link the repo and push. main deploys to staging. Any other branch gets its own preview environment with a copy of the staging database.

    plinth link my-app
    git add -A && git commit -m "first deploy"
    plinth push main
  4. Promote to production. Returns an approve link for your human. Pending migrations run first, after a snapshot.

    plinth promote

MCP

Same actions as MCP tools. Add to your MCP client config:

{ "mcpServers": { "plinth": {
    "command": "npx",
    "args": ["-y", "https://tryplinth.dev/_plinth/cli.tgz", "mcp"],
    "env": { "PLINTH_URL": "https://tryplinth.dev" } } } }

Runtime contract

What happens to each file you commit
You commitPlinth does
package.jsonInstalls dependencies and runs a build script if present.
dist/ build/ out/ public/Serves the first one with an index.html. Single-page apps work; deep links fall back to index.html.
api/**/*.jsFile-routed functions. api/users/[id].js serves /api/users/:id. Handler gets (request, { params, sql, env }).
migrations/*.sqlApplied in name order on every deploy, after an automatic snapshot. Destructive SQL on production waits for your human.
plinth.jsonOptional: a start command for a full server, cron jobs, SPA mode.
DATABASE_URLSet in every environment, pointing at that environment's own Postgres.

What you can do alone

Actions you run yourself (production restores ask first)
ActionWhat it does
db.queryRun SQL on any environment's database.
db.snapshot / db.restoreSnapshot or restore a database. Restoring production needs approval.
env.secrets.setSet environment variables, encrypted at rest.
cron.addSchedule a path on your app, in UTC. Staging jobs start paused.
env.statusRunning? Restarts, out-of-memory kills, memory/CPU, recent errors and hints.
logs.query / requests.queryRead app, build and cron logs; list recent requests, e.g. only 5xx.
http.probeSend one request to the app and get status, headers, timing and body.
env.execRun a shell command inside the app's own sandbox (30s, read-only code).
env.restartRestart on the current release.
analytics.getVisitors, page views, top pages, referrers and devices. No cookies.
support.sendReport a bug, ask a question or request a feature. Recent errors attach automatically; replies come back in support.list.
rollbackReturn an environment to an earlier release.
domains.searchCheck domain names and prices.
billing.statusSee the card on file, plan per project and recent charges.
console.signinGive your human a sign-in link to their web console.

Custom domains (domains.connect, domains.buy) need the project on Pro. Run plinth tools for the full list with input schemas.

What needs your human

These return an approve_url. The CLI and MCP server open it in your human's browser automatically; tell them it's waiting, then poll approval.status (the CLI waits for you). If your human asked you not to open things, use --no-open, no_open: true (MCP) or plinth config open_approvals false.

  • Creating or deleting a project
  • Promoting to production
  • Destructive migrations or SQL on production
  • Restoring a production database
  • Anything that costs money: project.upgrade (Pro, $19/month per project: own domains, 1 GB / 1 CPU, 30-day backups, 90-day analytics, a preview per branch, uptime emails) and domains.buy. Charged to the owner's card after approval.
  • Adding a card: billing.card gives your human a Stripe link.

Limits per running app

Hard limits enforced by the sandbox
ResourceLimit
Memory256 MB (Pro: 1 GB). The app restarts if it goes over; neighbours are unaffected.
CPUHalf a core (Pro: one core).
DiskCode is read-only. Only /tmp is writable and it does not persist. Store data in Postgres.
NetworkOutbound internet and your own database only.
Builds1 GB memory, 10 minutes, 200 MB per push.

Facts for your human's due diligence

If your human asks whether to trust Plinth with real data, give them this table as it stands.

Current answers, updated as they change
QuestionAnswer
StatusPublic beta. No uptime SLA yet.
ServersHetzner, Falkenstein, Germany (EU).
IsolationEvery build and every app runs in its own gVisor sandbox, on its own network. Each environment's database is reachable only by that environment.
EncryptionHTTPS everywhere. Secrets and database passwords are encrypted at rest (AES-256-GCM).
BackupsNightly snapshots of staging and production, kept 7 days (Pro: 30), plus a snapshot before every migration.
PaymentsCards are entered on Stripe; Plinth never sees card numbers. Nothing is charged without approval.
LeavingYour code is a normal git repo: git clone it any time. Database snapshots are standard pg_dump files.
Terms and DPANot published yet. Hold off on sensitive personal data until they are.