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 one central upstream Supabase release, owned by the manual Argo CD Application supabase. Its environment Kustomize overlay consumes the pinned community chart; the deleted in-repository Supabase component charts are not fallback paths. In Kubernetes, Postgres is not a per-Supabase StatefulSet: the durable SQL plane is the shared CloudNativePG cluster fractalops-postgresql.

  • Postgres (the durable data; backed up centrally): shared CloudNativePG fractalops-postgresql-rw
  • Kong API gateway (port 8000) — single ingress for all project schemas
  • Auth (GoTrue), PostgREST, Realtime, Storage (S3-compatible)
  • Studio + postgres-meta from the same upstream release

Central secrets live in OpenBao (runtime/supabase and narrowly scoped Realtime and Storage references), never in environment 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 Storage writes object bytes to the shared authenticated SeaweedFS S3 endpoint; the Supabase chart renders no database, MinIO, or Storage PVC. CNPG-managed roles and Backend Git migrations own the database prerequisites. A previous Storage volume may be kept read-only only for the one-time orphan handoff and verified object copy. It is not desired runtime state and must not be preserved through permanent Argo Force/Replace annotations.

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 — publish platform/k8s/bootstrap_supabase.sh, then manually sync the sole supabase Application after CNPG, SeaweedFS, backup, and capacity preflight. (assembly)
  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)

The chart source, component images, CNPG schema revision, and SeaweedFS bucket contract are pinned in Assembly. The bundled_database_service_forbidden wall blocks the per-project-DB anti-pattern.