Skip to content
AbloAblo Docs
Esc
navigateopen⌘Jpreview
On this page

Deployment

What production takes: a database Ablo can reach, a key minted for the plane you mean, and a schema push in the deploy.

One command answers whether the infrastructure needed for a write is ready right now, and if not, why:

ABLO_API_KEY=sk_… npx ablo status
  ablo status

  key     sk_51H8… (ABLO_API_KEY env — overrides stored)
  org     org_3nKq…
  project checkout (prj_7Yb2…)
  acts on production root
  push    production with sk_51H8… (env)
  api     https://api.abloatai.com  reachable
  data    ✓ database connected to this plane (direct)
  schema  4 models pushed (rev 12) hash 3f9a2c81 @ 2026-07-18
          • orders         typename=orders
          • lineItems      typename=lineItems
          • fulfilments    typename=fulfilments
          • reviews        typename=reviews

  ✓ write infrastructure is ready. Your database constraints and row-level policies still apply.

status asks the routing authority rather than sampling a read, because reads resolve while writes are held — a plane with no database connected serves every read and refuses every write. The verdict at the bottom is the whole page in one line, and --json puts the same conclusion in a blockers array you can gate a deploy on.

The three ingredients

There is no Ablo service for you to deploy. Ablo is hosted, your rows live in your own Postgres, and your app runs where it already runs — so a deployment is three pieces pointed at the same plane.

Ingredient Who runs it What “deploying” means for it
Your Postgres You (or your provider) Registering it against the production root, once, with a root-bound key.
Ablo Hosted at api.abloatai.com Nothing to run. You choose a project, a plane, and the keys that reach them.
Your app and agents You Holding the right credential for the runtime, and pushing the schema in the deploy.

Everything below is those three in order.

Planes: what a deployment targets

A plane is the isolation unit a credential acts on. production is the root branch; development and preview branches are children. Three things are per-plane, and knowing which three is most of what production readiness means:

  • Rows: a child-branch write is invisible to production and every sibling.
  • The registered database: one per plane, so your production database and your dev database are separate registrations.
  • The active schema artifact: the model shapes the engine actually routes on.

Every sk_ is fixed to one branch at mint. The persisted branch id—not a suffix or user-supplied slug—says whether that is the production root or one development child. This is why application code never passes an environment.

A child copies its parent’s active schema when it is created, then owns its schema history. A child push never changes production. Production gets new models only when the reviewed deployment pushes them to the root.

There is no shared development plane. New credentials are branch-bound; follow Branch-first development.

1. The database production writes to

Your production database joins Ablo the same way your dev database did — logical replication so Ablo can read and confirm, a scoped writer role so Ablo can land rows — run once, with a root-bound key so the registration attaches to the production root:

ABLO_API_KEY=sk_… npx ablo connect apply --url postgres://admin:…@host:5432/db
ABLO_API_KEY=sk_… npx ablo connect check

Connect Your Database is the full walkthrough — the SQL, the two roles, and the complete list of what Ablo touches. Five things about it are specifically production concerns:

Your agents do not each hold a connection. Every agent, worker, and function talks to Ablo, and Ablo holds the database connections — at most 4 connections per plane, the same 4 whether one caller is writing behind them or ten thousand are. They identify themselves as ablo-direct-writer, so pg_stat_activity accounts for everything Ablo has open at any moment. Size the database for that number rather than for your agent count.

Register the direct host, not the pooler. A pooler terminates the session that replication needs, and it refuses the connection in the same words a wrong password would — so a pooled host reads as a credentials problem for as long as you let it. ablo status names a pooled host when it sees one, with the direct host to use instead.

Reachability is measured from Ablo’s network, not yours. connect check runs from the infrastructure replication runs on, so an IPv6-only, IP-allowlisted, or VPC-private database still verifies — and a database your laptop can reach but Ablo cannot fails here rather than at the first write.

wal_level = logical needs a restart. It is server-wide and not reloadable. On RDS and Aurora it is a parameter-group change plus a reboot. Schedule it; it is the one setup step with downtime in it.

A replication slot retains WAL. While Ablo is connected the slot holds what it has not yet acknowledged, so a long disconnection accumulates disk. Ablo monitors slot lag and retention and surfaces it, and drops an abandoned slot rather than letting it grow without bound.

A database that cannot grant a REPLICATION role connects through the signed Data Source endpoint instead. Same model surface, same commit chokepoint — it is the marked fallback, so reach for it when replication is genuinely unavailable.

2. The credential each runtime holds

Credential configuration follows the runtime. Long-lived root keys use apiKey; scoped actors use session; browser login exchange uses session.endpoint:

Runtime Credential Notes
Server, worker, agent, cron sk_ in ABLO_API_KEY Defaults from the environment, so most code passes nothing.
Serverless function sk_ in ABLO_API_KEY Stateless request/response; nothing held open across invocations.
Long-running agent session: () => sessions.create(...) One renewable identity and one multiplexed WebSocket per client and Ablo cell; checkpoint durable deltas before acknowledging.
Browser, read-only root-bound pk_ Publishable, safe to ship, and read-only.
Browser, writing as the signed-in user session: { endpoint } A route on your backend mints a short-lived ek_ per user.

API Keys covers the model; Sessions covers minting. Two things bite specifically at deploy time.

ablo login does not give the application production authority. It stores a project-scoped mk_ management credential that manages branches and exchanges for temporary child credentials; it cannot read or write application data or push the production schema. A production deploy needs a secret sk_ from the dashboard, supplied as ABLO_API_KEY. ablo whoami names what an explicit key acts on, and ablo status --json reports its capability as runtimeKey.kind for a pipeline to check before pushing. There is no local mode switch.

An explicit key always wins. Application-facing commands resolve ABLO_API_KEY, then .env.local, then .env, then a legacy stored runtime credential — never the mk_ management login for data operations. ablo status prints what it found under key, with its source. When a deploy lands somewhere surprising, that line is usually the answer.

Use ablo whoami when you need only the authoritative identity:

ABLO_API_KEY=sk_… npx ablo whoami

For a deployment secret already stored under another name, prefer ablo whoami --key-env <NAME> over copying it into argv or permanently maintaining parallel local variables. The command reads the explicitly named value from the process or project env files and reports its project and branch without printing the full secret.

3. Pushing the schema is a deploy step

The server keeps its own copy of your schema and routes on that copy. Until it has yours, a write to a new model fails with server_execute_unknown_model — so ablo push belongs in your deploy pipeline, ordered before the code that depends on the new models goes live.

ABLO_API_KEY=sk_… npx ablo push --yes
# or, explicitly:
npx ablo push --env-file .env.production --yes

Production requires confirmation: interactively you type the destination project’s name, which is what makes a wrong-project deploy impossible to do by reflex. In CI there is no TTY, so --yes is the confirmation and a push without it stops rather than proceeding unattended.

Additive changes pass; destructive ones ask. Adding a model or an optional field applies cleanly. Dropping a model or a field, narrowing an enum, or a lossy cast is classified as data loss and needs --force; adding a required field to a populated table needs a --backfill. A push that fails is recorded failed and never activated, so a broken migration cannot leave clients gated against tables that do not match.

This is the same expand-and-contract shape any online migration has, and it sequences the same way: push the additive change, deploy the code that writes both shapes, backfill, then push the removal in a later deploy once nothing reads the old field.

Drift is a connect-time rejection, not a runtime surprise. A client built against a schema the server is no longer running is turned away when it connects. ablo status prints the local hash beside the deployed one, and the running client reports the same serverSchemaHash value, so the two can be matched at a glance.

Gate the deploy on the verdict

ablo status --json reports the same conclusion the human output ends with, in a form a pipeline can act on. An empty blockers array is the machine-readable form of “ready”:

blockers=$(ABLO_API_KEY=$ABLO_API_KEY npx ablo status --json | jq '.blockers | length')
[ "$blockers" -eq 0 ] || { npx ablo status; exit 1; }

Each blocker carries a problem and the single fix that resolves it, in the order you should act on them: an unreachable API makes every other finding unverifiable, and a plane with nothing connected makes a schema question academic. The JSON also carries confirmedTarget — the org, project, and environment the server says this key resolves to — which is the authoritative answer to where a push would land.

Webhooks point at the deployed URL

npx ablo dev prepares the schema branch; it does not forward webhooks. A deployed HTTPS endpoint is registered once, and Ablo returns the signing secret a single time:

ABLO_API_KEY=sk_… npx ablo webhooks create https://yourapp.com/api/ablo/[...all]
ABLO_API_KEY=sk_… npx ablo webhooks list   # endpoints + delivery health

webhooks list reports each endpoint’s status, cursor, and last error — the place to look when a mirror falls behind. Webhooks covers the handler, the Standard Webhooks signature, and rolling a secret.

Multi-stage schema changes

ablo plan is the read-only front door for source, active artifact, and PostgreSQL together. For a live rename, type transition, or required-field change, commit a deployment manifest and pass the same file to plan and push. Its gates are expand, dual_write, backfill, verify, switch, and contract; each names an owner, resource, dependencies, status, and action.

npx ablo plan --manifest ablo/deployment.json
npx ablo push --manifest ablo/deployment.json --yes

A live manifest cannot combine expand and contract for the same resource. Contract belongs in a later manifest with its own recorded approval. A backfill being finished is not contract evidence; verification and client-drain gates must also be satisfied.

What to watch once it is live

  • ablo logs: commit activity as it happens, scoped by the key. A root-bound key streams production; a child-bound key streams only that child. --json emits NDJSON for piping.
  • ablo status: the readiness verdict. Cheap enough to run from a health check on your own side.
  • The audit log: every confirmed write traced back to the key that made it and the person who authorized that key.
  • Your own logger: pass logger to the client and SDK lifecycle, sync, retry, and rollback events join your existing pipeline.

Writes carry receipts rather than being fire-and-forget: a commit is accepted the moment Ablo takes it (queued) and becomes confirmed once the row appears on your database’s WAL. Guarantees covers which state to wait for and what each promises.

When something is wrong

What you see What it means The fix
no database is connected to this plane Writes are held rather than routed. Reads still resolve, which is why a read probe stays quiet. ablo connect apply with a key for that plane.
password authentication failed during connect Often a pooled host refusing a session it cannot serve, in the words of a wrong password. Register the direct database host.
server_execute_unknown_model The plane’s active schema does not carry that model. ablo push with a key for that plane.
Clients rejected at connect The deployed schema and the client’s schema disagree. Push this tree, or deploy the revision the server is running.
project_scope_denied (403) The model belongs to another project in your org. Use a key minted for that project: a push cannot cross projects.
403 on ablo push The key authenticated but cannot author schema. A secret sk_; the mk_ stored by ablo login is management-only.

The checklist

  1. Production database registered against the production plane, direct host, and ablo connect check all green.
  2. A secret sk_ in the deploy environment as ABLO_API_KEY — never in a browser bundle.
  3. ablo plan reviewed, followed by fingerprint-gated ablo push --yes.
  4. ablo status --json gating the deploy on an empty blockers array.
  5. Browser clients on a root-bound pk_ or session.endpoint, not a secret key.
  6. Webhook endpoints registered at their deployed URLs, with the signing secret in your environment.

Next steps

  • Connect Your Database — the setup this page registers, in full.
  • Projects — one org, many apps, each with its own planes and keys.
  • API Keys — which credential each runtime holds, and what it may do.
  • CLI — every command, its flags, and the environment variables.
  • Operating on Your Database — which actions run freely and which belong to a human.
  • Debugging & Logs — watching claims, queueing, and grants while you build.

Was this page helpful?