Skip to content

Supabase Central Data Plane

Per-project databases are an anti-pattern here: every project standing up its own Postgres/Supabase stack inside the sandbox loses persistence on workspace teardown, fragments backups, and wastes capacity. The data plane must be one central, persistent, self-hosted Supabase; projects attach a scoped slice of it as a project asset, exactly like they attach a scoped Nexus registry or a Dokploy service. (The sandbox has no container runtime — docker is hard-walled — so there is no in-sandbox path to stand up a DB anyway.)

Two halves enforce this:

  • Negative (shipped): bundled_database_service_forbidden walls any agent handoff that bundles a postgres/supabase/… service into a project compose file. Agents cannot bring up their own DB; databases are provisioned on the project’s Dokploy plane through the service catalog, not from the sandbox.
  • Positive (this doc): the central Supabase stack + the supabase connector + the project asset give projects a real, managed, persistent database to target with supabase/migrations/.

Run the official Supabase self-hosting compose as a single central stack (managed like the other central stacks — Dokploy/Nexus — not per project):

  • Postgres (the durable data; backed up centrally) + Supavisor pooler
  • Kong API gateway (port 8000) — single ingress for all project schemas
  • Auth (GoTrue), PostgREST, Realtime, Storage (S3-compatible)
  • Studio + postgres-meta, optional Logflare + Vector

Central secrets live in OpenBao (connectors/supabase), never in env defaults: JWT_SECRET, SUPABASE_PUBLISHABLE_KEY (anon), SUPABASE_SECRET_KEY (service_role), POSTGRES_PASSWORD, SECRET_KEY_BASE, VAULT_ENC_KEY, PG_META_CRYPTO_KEY, S3 protocol keys.

Supabase OSS is not natively multi-tenant (one stack ≈ one project). The standard way to share one instance across projects is schema isolation, not a stack per project:

  • Each project gets a dedicated Postgres schema proj_<slug> in the central DB.
  • A scoped DB role per project (owner of its schema) + authenticated/anon grants limited to that schema; RLS enforces row scoping.
  • The schema is exposed through PostgREST via PGRST_DB_SCHEMAS (project schema added to the served list) behind the central Kong gateway.
  • The project receives a connection lease (scoped connection string + scoped keys), short-TTL and rotated — the same lease model as the project service catalog uses for Dokploy-backed services.

The project’s supabase/migrations/*.sql run against its schema on the central instance. Data persists centrally across workspace teardown.

Declared in connector-catalog.json as a hybrid, project-scoped connector (targets: ["supabase"]), mirroring Nexus. Role mapping uses Supabase’s real roles:

FractalOps roleSupabase role
project_owner / maintainer, solution_adminservice_role
project_contributor, team_member, solution_userauthenticated
project_viewer, solution_vieweranon

Actions (upsert_database_project_access / _team_ / _access) provision the project schema + scoped role + grants + PostgREST exposure, and issue the scoped connection lease.

A project adds the central Supabase as an asset (a repository/asset binding, requiredManagedServices: [postgres, auth, storage] already declared on the backend golden template). Attaching the asset = provisioning the project schema + lease via the connector. The project service catalog (service_catalog MCP / service add) binds the workspace to that scoped schema, instead of a bare per-project postgres stack.

  1. Central stack topology — deploy the official Supabase compose as a central stack (k8s/Dokploy), secrets in OpenBao connectors/supabase. (infra)
  2. supabase connector executor_supabase_executor in executor_factory: connect as admin, CREATE SCHEMA proj_<slug>, CREATE ROLE, grants, RLS, add to PGRST_DB_SCHEMAS, mint scoped lease. (connector)
  3. Project asset binding — project-factory adds Supabase as a scoped asset and calls the connector on attach. (project-factory)
  4. Deploy-plane — point the project service catalog’s supabase binding at the central instance schema instead of a bare dokploy/postgres stack. (integration)

Step 0 (connector declaration) is done; the bundled_database_service_forbidden wall already blocks the per-project-DB anti-pattern.