Skip to content
Public preview · the console is open

ggui deploy

read as .md

ggui deploy provisions and wires a hosted GGUI app for the current project in one pass: sign in, create the cloud app, mint a connector key, upload your local blueprint pool and app config, and write the endpoint into .env.local. It is the command form of everything the dev-loop crossover does by hand.

The command is idempotent. Each step is skipped when its gate is already satisfied, so re-running after a partial failure resumes from the first unsatisfied step instead of duplicating work.

From a directory containing (or under) a ggui.json:

Terminal window
ggui deploy

On completion it prints the app’s MCP endpoint and where the bearer landed:

ggui deploy complete.
App MCP endpoint: https://mcp.ggui.ai/apps/<appId>
Bearer key: in .env.local (GGUI_MCP_BEARER)

Your agent picks up the hosted endpoint on its next start — .env.local now carries both GGUI_MCP_URL and GGUI_MCP_BEARER, the same two variables the local ↔ hosted swap is built on.

Without a ggui.json anywhere up the directory tree, the command exits 1 before doing anything — it needs the manifest both to persist the appId and to know what to push.

Steps run in this order. The first three are gated on current state; the rest always run:

Step Runs when What it does
login no valid session in ~/.ggui/auth.json Device-flow sign-in to api.ggui.ai — same flow as ggui login.
create-app ggui.json has no appId POST /v1/apps, then persists the new appId into ggui.json. If the app was created but ggui.json could not be written, the command stops loudly with the id to add by hand — re-running without it would create a duplicate app.
mint-key .env.local has no GGUI_MCP_BEARER Creates a connector key (named ggui deploy) and writes its one-time-reveal secret into .env.local. The secret is shown once; only the prefix is retrievable later on the connector keys page.
push always Compiles the local blueprint pool and uploads it to the app — the same operation as ggui push --app <appId>. Artifact ids are deterministic (contractHash-variantKey), so re-pushing the same pool is a no-op server-side.
push-config always Pushes the app config declared in ggui.json: the gadget catalog, app.publicEnv (keys must match GGUI_PUBLIC_APP_<NAME>), the generation block (model, keySource) and the theme. Leaving generation or theme out leaves the app’s stored value untouched. Setting it to null clears the stored value, and the step prints generation cleared / theme cleared. Loopback bundle URLs are rejected — a hosted app cannot fetch from 127.0.0.1.
push-keys only with --push-keys Pushes your LLM provider API key to the app — see Provider keys below.
wire-env always Writes GGUI_MCP_URL into .env.local — unless one is already set, which is never clobbered.

.env.local lives next to ggui.json (not necessarily the invocation directory). Both files are edited in place; other keys and comments are preserved.

The per-app hosted endpoint is the bare per-app URL:

https://mcp.ggui.ai/apps/<appId>

There is no /mcp suffix — that suffix is the local ggui serve convention, and appending it to a hosted app URL produces a 404. Writing the correct shape automatically is the practical reason to prefer ggui deploy over hand-editing .env.local; the trap is documented in the dev loop.

Two details of wire-env:

  • First deploy — the URL comes verbatim from the connectUrl the backend returned at create-app, so it is correct for whichever environment GGUI_API_URL pointed at.
  • Re-deploys — an existing GGUI_MCP_URL is left untouched, whatever it points at. Delete the line and re-run if you want it rewritten.

Apps with generation.keySource: "own" render on your model account instead of drawing down GGUI credits — but only once the app actually holds your provider key. --push-keys adds that step to the deploy:

Terminal window
ggui deploy --push-keys

The provider is derived from ggui.json#generation.model, and the key is read from that provider’s environment variable:

Provider Environment variable
anthropic ANTHROPIC_API_KEY
openai OPENAI_API_KEY
google GOOGLE_API_KEY (falls back to GEMINI_API_KEY)
openrouter OPENROUTER_API_KEY

bedrock routes are rejected — IAM-only, no plaintext key to push. To name the provider explicitly instead of deriving it from the model string, call the underlying command directly: ggui provider-key set --app <appId> --provider <name>.

If ggui.json declares keySource: "own" and you deploy without --push-keys, the command still succeeds but ends with a warning: cloud renders will reject until a key is pushed. See Credits, billing & BYOK for how the two key sources are charged.

ggui deploy provisions the GGUI side only — the interface layer. Hosting the agent itself (the process that talks to your users and calls GGUI) is a separate concern: run it yourself, or use a hosting platform such as guuey. Nothing in this command deploys, restarts, or configures an agent runtime.

ggui deploy [--push-keys]
Flag Purpose
--push-keys Insert the push-keys step: push the provider API key from your environment to the cloud app.

GGUI_API_URL (see CLI configuration) controls which backend the deploy targets; the default is production api.ggui.ai.

  • ggui CLI overview — the full command surface, including standalone ggui push and ggui provider-key.
  • The dev loop — iterate locally, then cross over to hosted with the two-variable swap this command writes.
  • ggui login — the device-flow sign-in deploy runs when needed.
  • Connector keys — what the minted ggui_user_* key is and how to revoke it.
  • Blueprints & the cache — why uploading a matured local pool makes a fresh hosted app fast on day one.