graft. docs

Deploy

Self-host Graft — one container, split compose, or handlers embedded in your app.

Self-host Graft

Graft is self-hostable. You run the source against your Postgres and your object store. There is no required Graft cloud.

Container quick path

docker run -p 3903:3903 graft
# migrate → compile → serve
# GET /healthz reports branch + git SHA

Mount your project at /project. Boot also creates a hardened runtime role and serves under it, because this container owns its database. Full recipes live under deploy/ in the repo.

Topologies

Runtime work is a stateless Request → Response handler. Two wrappers, same bytes:

  1. Embedded — your app (Next, Astro, SvelteKit, TanStack Start, React Router) mounts the handlers as routes.

  2. Headlessgraft serve binds those handlers to a Node HTTP server: POST /api/fn/<name>, POST /api/mcp, GET /api/content/v1/documents, GET /api/content/v1/search, GET /healthz. A frontend can read authored content through @usegraft/content-api without a database of its own. See Reading content.

    The two content routes authenticate nobody, and they publish every collection in your config. They carry a 60-per-60s rate limit under graft serve, which is a backstop against exhaustion, not an access control. Anything reachable beyond a trusted proxy should be fronted by one that checks. See The content API.

Adapters

AdapterTopologyRuns
Railwaycontainerdeploy/docker image + managed Postgres / R2
Fly.iocontainerSame image on Fly Machines; Tigris or R2 for assets
VPScontainerSplit compose (db / storage / graft) behind a reverse proxy
VercelembeddedApp mounts handlers; compile is a build step

This docs site uses the Vercel embedded path from Astro.

Credential recipe

Give the deployment a runtime credential that can execute mounted endpoints but cannot decide approvals:

CREATE ROLE graft_runtime LOGIN PASSWORD '…';
graft harden graft_runtime
# deploy with the runtime role URL as DATABASE_URL
# keep the operator URL for compile / migrate / approve

The container automates this. All-in-one does it by default; GRAFT_MODE=serve waits for GRAFT_HARDEN=1 or GRAFT_RUNTIME_PASSWORD, because there the database is yours.

TaskCredential
graft compile, migrate --apply, merge, approveoperator
Functions, MCP (incl. content writes), reads, approval request/consumeruntime

Hardened deployments are functions-and-reads first: the runtime role cannot project content, so remote MCP writes need the operator path (CI or a writable self-host mount).

Next steps