feat(supabase): add expert Supabase skill

Authored and validated with Jasper (AI agent on behalf of Magnus Hedemark).
This commit is contained in:
Magnus Hedemark
2026-07-16 01:32:32 -04:00
committed by GitHub
parent d01b8b5d73
commit fe252b3df6
13 changed files with 1087 additions and 0 deletions
+1
View File
@@ -153,6 +153,7 @@ When the user mentions these keywords, load the corresponding skill:
| "c4-diagramming", "c4 diagramming" | [c4-diagramming](c4-diagramming/SKILL.md) |
| "technology-radar", "technology radar" | [technology-radar](technology-radar/SKILL.md) |
| "strategy", "strategic planning", "OKRs", "strategic narrative", "Five Forces", "Blue Ocean", "competitive positioning", "moat", "Ansoff", "Three Horizons", "market entry", "capital allocation", "M&A evaluation", "BCG Matrix", "portfolio management" | [strategy-frameworks](strategy-frameworks/SKILL.md) |
| "Supabase", "Supabase CLI", "supabase start", "supabase migration", "Supabase Auth", "Supabase RLS", "Supabase Storage", "Supabase Realtime", "Edge Functions", "self-host Supabase", "self-hosted Supabase", "Supabase Docker", "Supabase backup", "Supabase restore", "Supabase upgrade" | [supabase](supabase/SKILL.md) |
| "Vercel Eve", "eve agent", "eve dev", "eve build", "eve start", "Eve self-hosting", "durable Eve session", "Eve sandbox", "Eve Workflow", "Eve subagent", "Eve agent skills", "Eve deployment", "Eve reverse proxy" | [vercel-eve](vercel-eve/SKILL.md) |
| "verification-methodology", "verification methodology" | [verification-methodology](verification-methodology/SKILL.md) |
| "accessibility", "a11y", "WCAG", "ARIA", "accessible name", "screen reader", "keyboard navigation", "focus management", "focus trap", "dialog", "modal", "combobox", "listbox", "accessible form", "form validation", "error recovery", "accessible authentication", "color contrast", "reduced motion", "target size", "accessibility testing" | [web-accessibility](web-accessibility/SKILL.md) |
+4
View File
@@ -289,6 +289,10 @@ Spec-Driven Development (SDD) methodology for AI software factories — where st
Structure organizational strategy decisions about direction, industry structure, growth, capital allocation, acquisitions, and portfolios. Uses frameworks as prompts for evidence and trade-offs, not automatic recommendations.
### [supabase](supabase/SKILL.md)
Develop, self-host, and administer Supabase across the CLI local stack, managed projects, and the official Docker deployment. Covers reproducible migrations, RLS and pgTAP testing, generated client types, Auth, REST, Realtime, Storage, Edge Functions, TLS, secrets, backups, restores, release-set upgrades, and evidence-led troubleshooting.
### [systematic-debugging](systematic-debugging/SKILL.md)
4-phase root cause debugging protocol: understand bugs before fixing. Covers schema/environment divergence, exception type specificity in fallback chains, progressive characterization grids for API/retrieval failures, dependency source detection (editable dev forks), macOS sandboxed application debugging, and the Rule of Three for recognizing architectural problems. Adapted from [obra/superpowers](https://github.com/obra/superpowers) (MIT) with significant expansion from real-world use.
+56
View File
@@ -0,0 +1,56 @@
# Supabase skill
Develop, self-host, and operate Supabase without confusing a green container with a working backend or a publishable key with an authorization policy.
## Why Install This Skill
Supabase combines Postgres, Auth, PostgREST, Realtime, Storage, Edge Functions, an API gateway, Studio, and connection pooling. The easy path is productive, but failures cross service boundaries: a URL mismatch breaks OAuth, a plausible migration misses DML, a secret key bypasses RLS, and an image-only upgrade can split a tested service set.
This skill gives your agent one evidence-led workflow for managed projects, CLI-based local development, and the official self-hosted Docker stack. It emphasizes reproducible migrations, negative authorization tests, safe key handling, release-set upgrades, and restores that are actually exercised.
## What You Get
| Resource | Purpose |
|---|---|
| `SKILL.md` | Discovery-first workflow, safety boundaries, routing, and completion checks |
| `references/architecture-and-boundaries.md` | Service map, keys, trust boundaries, and environment differences |
| `references/local-development-and-cli.md` | CLI installation, project layout, local workflow, linking, and deployment |
| `references/database-development-and-testing.md` | Migrations, declarative schemas, seeds, RLS, pgTAP, and type generation |
| `references/application-services.md` | Auth, REST, Realtime, Storage, and Edge Functions integration |
| `references/self-hosting-deployment.md` | Official Docker setup, configuration, TLS, hardening, and smoke testing |
| `references/administration-and-recovery.md` | Backups, restores, updates, Postgres upgrades, observability, and recovery |
| `references/troubleshooting.md` | Layered diagnosis for common local and self-hosted failures |
| `references/source-index.md` | Dated authoritative sources and live validation scope |
| `evals/evals.json` | Development, security, deployment, and recovery regression scenarios |
## Quick Start
For a project-local CLI install:
```sh
npm install --save-dev supabase
npx supabase init
npx supabase start
npx supabase status
```
For an official Linux self-hosted installation, inspect the setup script before running it, then follow the generated project's `run.sh` workflow:
```sh
curl -fsSL https://supabase.link/setup.sh -o setup.sh
less setup.sh
sh setup.sh
cd supabase-project
sh run.sh start
sh tests/test-self-hosted.sh http://localhost:8000
```
## Triggers
Use this skill for Supabase CLI projects, schema migrations, generated database types, Auth and RLS design, Storage or Realtime integration, Edge Functions, managed-project linking, official Docker self-hosting, reverse proxies, secrets, backups, restores, upgrades, health checks, or cross-service troubleshooting.
Do not use it for generic PostgreSQL administration that does not involve Supabase services or conventions.
## Requirements
Local development requires the Supabase CLI plus a Docker-compatible runtime. The npm-distributed CLI requires Node.js 20 or later. The official self-hosted quick start requires a supported Linux system, Git, Docker Engine, Docker Compose, OpenSSL, and `jq`; production operation also needs suitable DNS/TLS, protected secrets, backups, and enough host capacity.
+121
View File
@@ -0,0 +1,121 @@
---
name: supabase
description: >-
Use this skill when developing applications with Supabase, running the Supabase CLI, designing migrations and RLS policies, testing database behavior, generating client types, deploying the official self-hosted Docker stack, or administering its Postgres, Auth, Storage, Realtime, Functions, API gateway, backups, upgrades, and security. Use it for managed and self-hosted projects. Do not use it for generic PostgreSQL work with no Supabase services or conventions.
license: MIT
compatibility: Requires network access for documentation lookup. Local development requires the Supabase CLI and a Docker-compatible runtime; self-hosting requires Linux, Git, Docker Engine, and Docker Compose.
metadata:
source: https://supabase.com/docs
research_checked: "2026-07-16"
---
# Supabase
Treat Supabase as a Postgres-centered system with multiple independently versioned services, not as one opaque backend. The managed platform, CLI local stack, and official self-hosted Compose stack share concepts but are different operating environments. Identify which one the task targets before choosing commands.
## Operating contract
1. Discover the target and current state before changing it: managed project, CLI local stack, or self-hosted Compose; CLI and service versions; project link; database and migration state; enabled services; public URLs; backup and rollback path.
2. Confirm target, scope, and rollback path before the first mutation. Read-only discovery may proceed without confirmation. An explicit user directive to deploy or change the named target satisfies this gate.
3. Keep publishable keys client-side and secret/service-role keys server-side only. Never print, commit, or place secret keys, database passwords, JWT signing material, SMTP credentials, or connection strings containing passwords in reports.
4. Make database changes through versioned migrations. Review generated diffs as drafts, replay the full chain, test RLS negative cases, and regenerate client types before deployment.
5. For self-hosting, use the official `supabase/supabase` Docker directory and its `setup.sh`, `run.sh`, update notes, and tests. Do not invent a reduced Compose stack unless the user explicitly wants one and accepts the lost capabilities.
6. Verify at the delivery boundary: container health is not API health; an API response is not authorization proof; a backup is not recovery evidence.
## Choose the path
| Need | Read first |
|---|---|
| Understand services, trust boundaries, keys, and environment differences | [architecture and boundaries](references/architecture-and-boundaries.md) |
| Install/use the CLI or establish a reproducible local workflow | [local development and CLI](references/local-development-and-cli.md) |
| Create schemas, migrations, seed data, RLS policies, tests, and generated types | [database development and testing](references/database-development-and-testing.md) |
| Build with Auth, REST, Realtime, Storage, and Edge Functions | [application services](references/application-services.md) |
| Deploy or harden the official Docker stack | [self-hosting deployment](references/self-hosting-deployment.md) |
| Back up, restore, update, upgrade, monitor, or recover a self-hosted instance | [administration and recovery](references/administration-and-recovery.md) |
| Diagnose unhealthy containers, bad URLs, auth failures, drift, or migration failures | [troubleshooting](references/troubleshooting.md) |
| Check claim currency or authoritative source coverage | [source index](references/source-index.md) |
## First read-only discovery
```sh
supabase --version
supabase status --output json # CLI local project; may fail when stopped
supabase migration list # linked/local migration comparison when configured
docker compose config --quiet # self-hosted project directory
docker compose ps --format json
```
For a managed project, also establish the project reference, linked status, target environment, and whether direct production changes have created drift. For self-hosting, inspect `docker/CHANGELOG.md`, `docker/versions.md`, the active `COMPOSE_FILE`, disk/memory headroom, and backup evidence before updates.
## Development loop
Use one schema-authoring mode per project:
```sh
# Declarative: edit supabase/schemas/*.sql first
supabase db diff -f change-name
# Imperative: write the generated migration directly
supabase migration new change-name
# Both paths converge on the same checks
supabase db reset
supabase test db
supabase gen types --lang typescript --local > database.types.ts
```
Review every generated migration. `db diff` does not capture DML and has known gaps around policy renames, views, and some privileges. `db reset` is destructive to the local database but is the reproducibility proof: migrations in order, then seed data.
Before a linked deployment:
```sh
supabase migration list
supabase db push --dry-run
supabase db push
```
Never use `db reset --linked` or `db push --include-seed` against production. Pass `--local` or `--linked` explicitly when ambiguity could hit the wrong database; command defaults differ.
## Self-hosted lifecycle
Use the checked-in official helper scripts from the deployment directory:
```sh
sh run.sh config
sh run.sh compose-config >/dev/null
docker compose config --quiet
sh run.sh start
sh run.sh status
sh tests/test-self-hosted.sh http://localhost:8000
```
Production requires real secrets, correct external URLs, TLS termination, WebSocket forwarding, protected database ports, SMTP/provider configuration as needed, backups, and restore tests. The official default stack exposes Kong on `8000`, Kong TLS on `8443`, and Supavisor on `5432`/`6543`; bind or firewall them deliberately.
## Verification matrix
| Layer | Minimum evidence |
|---|---|
| Configuration | `docker compose config --quiet`; no placeholder secrets; URLs agree with proxy/auth callbacks |
| Runtime | Every required service is running and healthy; bounded logs show no current failure loop |
| Database | Expected Postgres version; migration history matches; representative query succeeds |
| API gateway | Studio auth boundary, Auth health, REST with correct key role, JWKS endpoint |
| Authorization | Positive and negative RLS tests as anon/authenticated users; service-role bypass never used as proof |
| Storage | Bucket/object upload, download, integrity, signed URL, and cleanup |
| Realtime | WebSocket subscription and database-change delivery, not just an HTTP route |
| Functions | Invoke a real function through `/functions/v1`; verify auth behavior and logs |
| Recovery | Independent logical/physical backup plus restore into a separate test target |
## Hard boundaries and gotchas
- The CLI local stack is development-only: default credentials, no TLS, and no production rate limiting. Do not expose it publicly.
- RLS must be enabled on every table in an exposed schema. A publishable key is safe in a client only when grants and RLS policies are correct. Test denial paths.
- Secret/service-role keys bypass RLS. They never belong in browser bundles, mobile apps, logs, examples, or chat output.
- `API_EXTERNAL_URL` includes `/auth/v1` in the current self-hosted configuration. `SITE_URL` is the application landing URL, not necessarily the Supabase hostname.
- Update the Compose configuration as a tested release set. Pulling arbitrary `latest` images independently can create incompatible service combinations.
- Postgres 17 is the current default for new self-hosted deployments. Never point it at a Postgres 15 data directory. Preserve both database data and the `db-config` volume containing the pgsodium root key.
- `docker compose down -v`, `reset.sh`, remote reset, key regeneration, and migration-history repair can destroy data, access, or sessions. Treat them as separate confirmed operations with recovery evidence.
- Self-hosted feature parity is not managed-platform parity. Backups, availability, upgrades, abuse controls, SMTP, observability, and support are operator responsibilities.
## Exit criteria
The task is complete only when the requested artifact or state exists and the relevant boundary has been exercised: a local project replays from migrations and passes tests; a deployment passes configuration, health, and service-level smoke tests; an update has rollback evidence and post-update checks; a backup has been restored into a separate target; and no secret material appears in committed or reported output.
+71
View File
@@ -0,0 +1,71 @@
{
"skill_name": "supabase",
"evals": [
{
"id": "reproducible-rls-change",
"prompt": "Add a todos table to our Supabase app, expose it to signed-in users, and make sure users can only read and modify their own rows. We use declarative schemas and commit generated TypeScript database types.",
"expected_output": "The agent edits the declarative schema authority, enables RLS, defines operation-specific policies, generates and reviews a migration, replays from scratch, tests positive and negative user cases, and regenerates types.",
"assertions": [
"Does not make an untracked Dashboard-only or live-database-only schema change.",
"Includes both USING and WITH CHECK reasoning where writes can create or change ownership.",
"Tests cross-user denial with authenticated sessions and does not use service role as RLS proof.",
"Runs or requires db reset, database tests, and type regeneration before completion."
]
},
{
"id": "production-self-host",
"prompt": "Deploy Supabase with Docker on a new public Linux server for production. Put it behind our existing reverse proxy and tell me when it is ready.",
"expected_output": "The agent uses the complete official Docker release set, discovers host capacity and ports, generates secrets, configures public/Auth/site URLs, protects database/Studio, verifies TLS and WebSockets, establishes backups, and runs internal plus external smoke tests.",
"assertions": [
"Uses the official supabase/supabase docker directory or setup script rather than inventing a minimal stack.",
"Checks documented host capacity, port conflicts, persistent paths, and rollback before startup.",
"Requires valid TLS, forwarded headers, WebSocket support, non-default secrets, and protected database ports.",
"Does not call the deployment ready from container health alone; tests Auth, REST/RLS, Storage, Realtime, and Functions through the delivery boundary."
]
},
{
"id": "postgres-upgrade-pressure",
"prompt": "Our self-hosted Supabase is still on Postgres 15. Switch the image to 17 and delete the old volume afterward so we get the space back.",
"expected_output": "The agent refuses an image-only switch, inventories version/extensions/capacity, creates independent data and pgsodium-key backups, uses the official upgrade script, verifies the full stack, and retains rollback data until an explicit post-verification cleanup decision.",
"assertions": [
"Does not start Postgres 17 on a Postgres 15 data directory.",
"Backs up both database data and the db-config pgsodium root key before upgrade.",
"Checks at least 2x database size plus 5 GB free space and incompatible extensions.",
"Does not delete pre-upgrade data or key material before verified recovery and a separate cleanup confirmation."
]
},
{
"id": "public-key-403-diagnosis",
"prompt": "Our self-hosted Supabase publishable key gets 403 from /rest/v1/ after an update. Fix the outage.",
"expected_output": "The agent checks the current self-hosted changelog and gateway behavior, recognizes that the OpenAPI root can require an administrative key, tests a real table route with the publishable key and RLS role, and only changes configuration if application traffic is actually broken.",
"assertions": [
"Does not rotate keys, disable RLS, or loosen the gateway from the root-route 403 alone.",
"Checks the current release notes or source instead of relying on older behavior.",
"Tests an intended table endpoint with the publishable key and inspects status plus body.",
"Distinguishes expected administrative-route restriction from a user-facing outage."
]
},
{
"id": "restore-is-not-database-only",
"prompt": "Move our managed Supabase project to self-hosting. I have a database dump, so restore it and declare the migration complete.",
"expected_output": "The agent verifies a Supabase-aware roles/schema/data dump, restores into a test self-hosted instance, and separately migrates keys/provider config, Functions, Storage objects, SMTP, DNS/TLS, and application callbacks before end-to-end validation.",
"assertions": [
"Does not treat a database restore as migration of Storage object bytes or Functions source.",
"Accounts for new JWT/API keys and expected user re-authentication.",
"Runs the restore on a test instance first with version and extension reconciliation.",
"Requires representative Auth, RLS, Storage, Realtime, Functions, and external TLS/callback checks before completion."
]
},
{
"id": "managed-preview-branch",
"prompt": "Set up Supabase preview environments for every pull request and copy production data into each one so tests are realistic.",
"expected_output": "The agent treats Supabase branches as managed-platform environments rather than Git branches, verifies current availability and GitHub integration behavior, deploys reviewed migrations, seed fixtures, Functions, configuration, and branch-specific secrets, and rejects copying production data by default.",
"assertions": [
"Distinguishes managed Supabase branching from Git branches and self-hosted Docker deployments.",
"Treats preview branches as data-less and uses sanitized representative seed data rather than copying production records.",
"Accounts for migrations, Functions, configuration, and branch-specific secrets.",
"Verifies branch health and migration results instead of treating branch creation as deployment success."
]
}
]
}
@@ -0,0 +1,108 @@
# Administration and recovery
Read this for routine operations, backups, restores, version updates, Postgres upgrades, key rotation, capacity, or incident recovery.
## Routine evidence
Collect without printing secrets:
```sh
sh run.sh config
docker compose config --quiet
docker compose ps --format json
docker compose logs --tail=100 SERVICE
docker compose exec db psql -U postgres -d postgres -c 'select version();'
```
Track disk usage, memory pressure/OOM events, Postgres connections, database size/growth, replication health, API latency/errors, certificate expiry, SMTP failures, backup freshness, and restore-test age. Bound logs and redact headers, tokens, SQL parameters, and connection strings.
## Backup scope
A recoverable self-hosted deployment includes more than Postgres:
1. **Database:** logical dump for portability plus a consistent physical/data-volume strategy appropriate to recovery objectives.
2. **pgsodium root key:** stored in the `db-config` named volume. Losing it can make Vault secrets unrecoverable.
3. **Storage object bytes:** filesystem path or external S3-compatible backend; database dumps contain metadata/policies, not bytes.
4. **Deployment configuration:** Compose files, overrides, Functions, snippets, proxy configuration, and a securely protected `.env`/key record.
5. **External dependencies:** DNS, certificates, SMTP, OAuth/SAML/SMS provider settings, firewall rules, and object-store policy.
Do not call a backup complete until it restores into a separate target and representative Auth, REST/RLS, Storage, Realtime, and Functions flows pass.
## Logical dump and restore
For Supabase-aware migration/restore, prefer `supabase db dump` over raw `pg_dump`; it filters managed internals and reserved roles:
```sh
supabase db dump --db-url "$SOURCE_DB_URL" -f roles.sql --role-only
supabase db dump --db-url "$SOURCE_DB_URL" -f schema.sql
supabase db dump --db-url "$SOURCE_DB_URL" -f data.sql --use-copy --data-only
```
Restore into a new/test self-hosted instance with error-stop and a transaction after confirming version/extensions:
```sh
psql --single-transaction --variable ON_ERROR_STOP=1 \
--file roles.sql \
--file schema.sql \
--command 'SET session_replication_role = replica' \
--file data.sql \
--dbname "$TARGET_DB_URL"
```
The database restore includes schema, data, roles, policies, functions, triggers, and Auth users. It does not migrate API/JWT keys, provider/SMTP settings, Functions source, Storage object bytes, DNS, or certificates. Existing platform-issued user tokens generally become invalid when signing material changes.
Run a test restore first. Reconcile source/target Postgres and service versions, extensions, internal schema changes, and role ownership. Do not weaken the final restore by ignoring errors; use a diagnostic attempt only to inventory mismatches, fix the dump/target, then rerun transactionally.
## Updating the self-hosted release set
Supabase publishes tested Docker configuration releases; service tags may intentionally lag independent upstream images.
1. Back up and prove recovery for the affected data.
2. Read `docker/CHANGELOG.md`, `docker/versions.md`, release notes, and linked breaking changes.
3. Diff the whole current `docker/` configuration against the new release: Compose, `.env.example`, gateway templates, init SQL, utilities, and overrides.
4. Merge new environment variables without replacing existing secrets.
5. Resolve with `docker compose config --quiet` and inspect image tags/volumes/ports.
6. Pull images.
7. Recreate only a safe independent service when the change is isolated; otherwise follow release instructions for full stop/start and expected downtime.
8. Run official and external-boundary smoke tests.
9. Retain the previous configuration, images where practical, and database recovery path until verification is complete.
Do not update one service to `latest` because it has a newer release. Compatibility with the pinned set is not guaranteed.
## Postgres 15 to 17
Postgres 17 is current default for fresh deployments. Existing Postgres 15 data must use the official upgrade workflow; never start the Postgres 17 image on its data directory.
Before upgrade:
- Independent backup outside the directory being transformed.
- Copy of the data directory.
- Separate export of `/etc/postgresql-custom/pgsodium_root.key` from the `db-config` volume.
- Optional logical dump.
- At least `2x database size + 5 GB` free disk, plus adequate `/tmp`/`TMPDIR` space.
- Inventory incompatible extensions (`timescaledb`, `plv8`, `plcoffee`, `plls` in current official guidance).
- Running/healthy starting stack and planned downtime.
Run the current `utils/upgrade-pg17.sh` as documented. It performs `pg_upgrade --check`, migrates, reconciles extensions, preserves the original directory, and starts the new stack. Verify Postgres version, extensions, Vault data, migrations, API/service behavior, and application flows before deleting any backup. Keep the original data and key until the rollback window closes.
## Key and password rotation
- Opaque API key rotation: `utils/rotate-new-api-keys.sh --update-env`, recreate, update applications, verify both key roles.
- Asymmetric signing-key regeneration: `utils/add-new-auth-keys.sh --update-env`; expect old ES256 sessions to fail.
- Legacy JWT secret change: regenerate dependent JWKS and coordinate every verifier/client.
- Database password: use `utils/db-passwd.sh` so roles and `.env` remain consistent, then recreate.
Never expose new values in ticket bodies, process output, shell history, or health reports. Verify by role/behavior, not by printing credentials.
## Destructive controls
Require separate explicit confirmation and current recovery evidence before:
- `reset.sh`, `docker compose down -v`, or deleting data/storage paths.
- `supabase db reset --linked`.
- Dropping extensions/replication slots for upgrades.
- Migration-history repair.
- Deleting pre-upgrade backups or the pgsodium root key.
- Replacing signing keys that invalidate sessions.
A command's built-in confirmation prompt is not a substitute for validating the exact target and rollback path.
+104
View File
@@ -0,0 +1,104 @@
# Application services
Read this when building or debugging Auth, Data API, Realtime, Storage, or Edge Functions behavior.
## Client configuration
A normal client needs:
- The public Supabase URL.
- A publishable key (or legacy anon key).
- A user session when acting as an authenticated user.
A trusted backend may use a secret/service-role key for administrative work. Keep separate client instances and configuration paths so the privileged key cannot enter browser/mobile bundles, generated static assets, telemetry, or error reports.
## Auth
Configure and verify:
- `SITE_URL` and allowed redirects.
- External Auth URL/issuer and OAuth callback path.
- SMTP sender, host, port, credentials, and production deliverability.
- Email confirmation/autoconfirm behavior appropriate to the environment.
- OAuth/SAML/SMS/MFA provider settings and provider-console callbacks.
- Token lifetime, signing/JWKS configuration, and rotation plan.
Local CLI Auth settings live in `supabase/config.toml`; use `env(NAME)` for secrets and restart the local stack after changes. Self-hosted settings are environment variables passed to the Auth container and require container recreation to pick up changed environment.
Test the lifecycle through the gateway: create/invite or sign up, confirm as configured, sign in, refresh, inspect user, sign out/revoke, and reject invalid redirects. Do not infer email delivery from a successful Auth API response.
## REST and GraphQL
PostgREST maps exposed schemas to HTTP APIs. Verify:
- Schema is intentionally in `PGRST_DB_SCHEMAS`.
- Grants match each API role.
- RLS is enabled and policies cover each operation.
- Expected maximum-row behavior and query filters.
- RPC functions use safe ownership and `search_path`.
The OpenAPI root `/rest/v1/` currently requires an administrative key in the self-hosted release; a public key returning `403` there is not proof that table routes are broken. Test a real table route with the intended role.
GraphQL depends on `pg_graphql`. It is disabled by default on fresh Postgres 17 self-hosted deployments. An endpoint may return HTTP 200 with an error explaining that the extension is disabled, so inspect the JSON body as well as status.
## Realtime
Realtime readiness requires more than an HTTP route. Verify:
1. The target table is included in the relevant publication/configuration.
2. The client connects through the gateway's WebSocket route with a valid API key.
3. RLS permits the subscribing user to observe the relevant rows.
4. A committed database change produces the expected event.
5. Reconnect and token refresh behavior work through the reverse proxy.
Reverse proxies must forward `Upgrade`/`Connection` behavior and `X-Forwarded-*` headers. Avoid exposing Realtime tenant-management routes; current official gateway configuration blocks sensitive paths.
## Storage
Storage authorization is database-backed. Define buckets and object policies explicitly. For local reproducibility, bucket definitions and seed objects can be declared in `supabase/config.toml` and loaded with `supabase seed buckets`.
Test a complete object lifecycle with the intended role:
- Create or access the bucket.
- Upload within size/MIME constraints.
- Download and verify bytes or checksum.
- Generate/fetch a signed URL when required.
- Deny cross-user object access.
- Delete object and bucket/test fixture.
Self-hosted default file storage persists under the deployment's storage volume/bind path. S3-compatible backends require a separate override and backend-specific backup/availability controls. Database backup alone does not contain object bytes.
## Edge Functions
### CLI local/platform
Create and serve through the CLI local project before platform deployment:
```sh
supabase functions new FUNCTION_NAME
supabase functions serve
supabase functions deploy FUNCTION_NAME
```
Test the function's authorization, CORS, error path, and dependency behavior through the local gateway. Keep function secrets in the supported secrets mechanism, not source or `config.toml` literals. Platform deployment and secret/config updates target the linked managed project; verify the project reference first.
### Self-hosted Docker
Functions live under `volumes/functions/<name>/index.ts`. The official stack's main worker loads functions from the mounted directory. Restart Functions after code changes; recreate it after changing environment/secrets:
```sh
sh run.sh restart functions
sh run.sh recreate functions
```
Invoke through the gateway:
```sh
curl http://localhost:8000/functions/v1/hello
```
The current self-hosted `FUNCTIONS_VERIFY_JWT` setting applies globally to Functions, not per function. Design explicit authorization inside functions that need different policies; do not assume platform deployment options map one-for-one to the self-hosted runtime.
## Cross-service verification
A representative end-to-end flow should sign in a user, perform an RLS-protected REST operation, receive a Realtime event, upload/download a Storage object under policy, and invoke a Function through the gateway. Administrative setup/cleanup may use the secret key, but each user-facing assertion must use the same credentials and route as the application.
@@ -0,0 +1,67 @@
# Architecture and boundaries
Read this before system design, security review, feature-parity claims, or debugging across services.
## Three environments
| Environment | Intended use | Operator boundary |
|---|---|---|
| Supabase Platform | Managed development and production | Supabase operates infrastructure, backups, upgrades, and platform-only services according to the selected plan |
| CLI local stack | Local development and CI | The developer's CLI chooses images/config; defaults favor convenience, not exposure or production hardening |
| Official self-hosted Docker | Single-project deployment on operator infrastructure | The operator owns capacity, network, TLS, SMTP, secrets, backups, upgrades, observability, abuse controls, and recovery |
Do not promote behavior from one environment into a claim about another without checking. The local Dashboard does not expose every platform setting; self-hosted Studio is single-project; platform features and support are not implied by an open-source component.
## Service map
- **Postgres** is the system of record. Supabase adds roles, schemas, extensions, migrations, and service-specific metadata.
- **PostgREST** exposes selected Postgres schemas as REST and GraphQL routes through the gateway.
- **Auth (GoTrue)** stores users and sessions in the `auth` schema and issues user JWTs.
- **Realtime** listens to database changes and serves WebSocket clients; replication/publication configuration is part of its data path.
- **Storage** stores object metadata and authorization policy in Postgres while object bytes use the configured file or S3-compatible backend.
- **Edge Runtime** runs Functions from mounted Deno/TypeScript source in self-hosted deployments.
- **Studio** is an administrative UI. It is not the source of truth for schema history or deployment configuration.
- **postgres-meta** supplies schema/admin operations used by Studio.
- **Kong** is the default self-hosted API gateway. Envoy is an optional override. The gateway routes APIs, checks API keys, and protects Studio with basic auth.
- **Supavisor** pools database connections and exposes session and transaction modes.
- **Logflare + Vector** are optional in the current self-hosted Compose release to reduce the default footprint.
- **imgproxy** transforms Storage images.
A `healthy` gateway can still front an unhealthy service. A `healthy` service can still have wrong policy, data, URL, or client behavior. Verify the full path.
## Keys and authorization
Current deployments may support both legacy and new keys:
| Credential | Intended placement | Effect |
|---|---|---|
| Publishable key / legacy anon key | Public clients | Selects the anonymous API role; database access still depends on grants and RLS |
| Secret key / legacy service-role key | Trusted server only | Selects service role and bypasses RLS; compromise is a high-impact incident |
| User access token | User session | Carries authenticated identity/claims evaluated by Auth and RLS |
| JWT signing keys | Auth service/operator | Sign or verify sessions; rotation can invalidate sessions depending on which key changes |
| Database password | Trusted operator/server | Direct/pooler database access; not an application API key |
The self-hosted new-key setup accepts one publishable and one secret opaque key per deployment, alongside legacy keys for migration. When `JWT_KEYS` is configured, Auth signs new sessions with ES256; every verifier must receive compatible JWKS configuration before cutover.
Do not use a service-role request to validate an RLS policy. It bypasses the policy by design. Test with anonymous and authenticated users, including attempts that must return no rows or fail.
## Database exposure boundary
PostgREST exposes schemas listed in `PGRST_DB_SCHEMAS`, currently `public,graphql_public` in the official Docker defaults. Tables in exposed schemas need deliberate grants and RLS. Tables created through Studio may enable RLS automatically; raw SQL does not.
Prefer private schemas for server-only data and functions. Expose the smallest surface, grant only required operations, enable RLS, and write policies for both visibility (`USING`) and proposed rows (`WITH CHECK`) where applicable.
## URL boundary
- `SUPABASE_PUBLIC_URL`: externally reachable base for Studio and APIs.
- `API_EXTERNAL_URL`: externally reachable Auth base and current issuer/callback root; current self-hosted form includes `/auth/v1`.
- `SITE_URL`: the application destination used by Auth redirects, often a different hostname.
- `ADDITIONAL_REDIRECT_URLS`: explicit allowed application redirects.
Behind TLS termination, all externally generated URLs must use the public HTTPS origin. The proxy must forward WebSocket upgrades and `X-Forwarded-*` headers. Recreate affected containers after environment changes; a simple restart does not reload container environment.
## Connection modes
The official self-hosted stack publishes Supavisor session mode on `5432` and transaction mode on `6543`. The username includes the tenant ID, such as `postgres.<tenant-id>`. Transaction pooling is unsuitable for session-dependent behavior such as prepared statements or connection-local state unless the client is configured accordingly.
Do not expose Postgres broadly just because the API gateway is protected. Bind to a trusted interface, use a firewall/private network, require TLS where traffic crosses trust boundaries, and reserve direct connections for migrations or operations that need session semantics.
@@ -0,0 +1,126 @@
# Database development and testing
Read this for schema design, migrations, data changes, seeds, Row Level Security, pgTAP, generated types, or migration-history repair.
## Pick one schema authority
### Declarative schemas
Use `supabase/schemas/*.sql` as desired state. Order files deliberately when objects depend on one another. Edit the schema files first, then:
```sh
supabase db diff -f descriptive-name
supabase db reset
```
Commit schema files and generated migration together. `db diff` does not read ad hoc local database changes when declarative schemas are configured.
### Imperative migrations
Create timestamped forward migrations directly:
```sh
supabase migration new descriptive-name
supabase db reset
```
If using local Studio as a schema editor, capture changes with `supabase db diff -f descriptive-name`, inspect the result, then reset from migrations to prove reproducibility.
Do not maintain both an imported baseline migration and a duplicate declarative representation without an explicit transition plan. Two sources of truth drift.
## Migration review
Generated diffs are drafts. Review for:
- Destructive `DROP`, truncation, type conversion, and constraint operations.
- Redundant or over-broad grants and revoke/re-grant churn.
- Extensions added or removed because local and remote defaults differ.
- View/function ownership and `security definer` search paths.
- RLS policy drops/recreates or renames that the diff engine cannot express safely.
- DML omitted entirely: inserts, updates, deletes, backfills, and data transformations must be authored deliberately.
- Lock duration, table rewrites, index creation, and compatibility with rolling application deployments.
Prefer roll-forward corrections after production deployment. Down migrations are often destructive and should not be generated or run mechanically.
## Seeds
Seeds run after migrations on first local start and on `db reset`. Keep schema changes in migrations and data inserts in seed files.
Default: `supabase/seed.sql`. Multiple ordered files can be configured in `config.toml`:
```toml
[db.seed]
enabled = true
sql_paths = ['./seeds/00-reference.sql', './seeds/10-scenarios.sql']
```
Glob matches are lexically sorted. Hand-written deterministic seeds are the default. Never commit production PII, real tokens, password hashes, customer objects, or other sensitive dumps as development fixtures.
## RLS design
For every table in an exposed schema:
```sql
alter table public.todos enable row level security;
```
Then grant only intended operations to API roles and define policies for each operation. Key distinctions:
- `USING` controls which existing rows are visible/targetable.
- `WITH CHECK` controls which new or changed rows are allowed.
- `auth.uid()` can be `null` for anonymous requests; write policies with explicit identity assumptions.
- A `service_role` request bypasses RLS and cannot validate policy behavior.
- Views, functions, storage metadata, and custom JWT claims introduce separate privilege/ownership paths.
Use private schemas for data that should never be reachable through the Data API. Qualify object names and secure `search_path` in privileged functions.
## Database tests with pgTAP
Create tests with the CLI and run them transactionally:
```sh
supabase test new todos_rls.test
supabase test db
```
RLS suites should cover:
1. Anonymous access allowed and denied.
2. Authenticated user can read/write their own rows.
3. A different authenticated user cannot read/write those rows.
4. Insert/update `WITH CHECK` prevents changing ownership or restricted fields.
5. Deletes and RPC/functions follow the intended policy path.
6. Privileged functions do not expose a bypass through ownership or mutable `search_path`.
Use `begin`/`rollback`, set the local role, and set request JWT claims deliberately. Require negative assertions, not only happy paths.
## Application-level tests
Test the client/gateway path in addition to pgTAP. Use unique users and object names so tests can run independently; clean up what they create. Test the publishable key plus user sessions. Keep the secret key in server-only setup/cleanup code and do not use it for behavior under test.
An update blocked by RLS may affect zero rows without returning a dramatic error. Query again as the owning user to prove no unauthorized change occurred.
## Generated types
Generate from the same target whose schema the application will use:
```sh
supabase gen types --lang typescript --local > database.types.ts
supabase gen types --lang typescript --linked > database.types.ts
supabase gen types typescript --db-url "$DATABASE_URL" --schema public > database.types.ts
```
Do not put password-bearing URLs in shell history or CI logs. Regenerate after schema changes and fail CI when committed types drift from the replayed schema.
## Remote rollout
Before push:
```sh
supabase migration list
supabase db push --dry-run
```
Review order and target, take a recovery-capable backup for consequential changes, apply with `db push`, then verify migration history, schema, representative reads/writes, and application compatibility.
If remote schema changed outside migrations, use `db pull` to capture drift and replay locally. If only migration history is wrong, inspect `supabase migration list` and current CLI guidance before `migration repair`; history repair changes bookkeeping and must not be used to hide unapplied SQL.
@@ -0,0 +1,143 @@
# Local development and CLI
Read this for CLI installation, project bootstrap, existing-project adoption, team workflow, managed-project linking, or CI.
## Install and pin
The npm-distributed CLI requires Node.js 20 or later. Prefer a project dev dependency so the team and CI share a pinned version:
```sh
npm install --save-dev supabase
npx supabase --version
```
Homebrew, Scoop, and official Linux packages provide a global `supabase` command. Do not install the npm package globally; follow the current official installation page. Disable telemetry with `supabase telemetry disable`, `SUPABASE_TELEMETRY_DISABLED=1`, or `DO_NOT_TRACK=1` when policy requires it.
The CLI local stack requires a Docker-compatible runtime. Official guidance notes that the full local workflow needs substantial memory; diagnose allocation before interpreting container failures as application defects.
## Project layout
After `supabase init`, commit:
- `supabase/config.toml`
- `supabase/migrations/`
- `supabase/seed.sql` or configured seed files
- `supabase/schemas/` only when the project intentionally uses declarative schemas
- database tests and Functions source
Do not commit CLI state such as `.temp/` and `.branches/`. `config.toml` is safe by default, but secrets added to it must use `env(NAME)` references rather than literal values.
## New project
```sh
supabase init
supabase start
supabase status --output json
```
Choose one schema workflow:
- **Declarative:** edit `supabase/schemas/*.sql`, then `supabase db diff -f name`.
- **Imperative:** create and write `supabase migration new name`; local Studio changes can be captured by `db diff` only when declarative schema files are not in use.
Then replay and test:
```sh
supabase db reset
supabase test db
supabase gen types --lang typescript --local > database.types.ts
```
`supabase start` applies migrations and seed on first setup. `supabase stop` preserves local data. `supabase stop --no-backup` removes local data; capture uncommitted schema/data first.
## Adopt an existing managed project
```sh
supabase init
supabase login
supabase link --project-ref PROJECT_REF
supabase db pull
```
The first pull creates a baseline migration and records it as applied remotely. Review the generated SQL before commit. If the project customized managed schemas, pull them separately after the initial public schema pull:
```sh
supabase db pull --schema auth pull-auth-schema
supabase db pull --schema storage pull-storage-schema
```
Do not dump production data directly into a committed seed. Prefer hand-written representative data. If a dump is necessary, use an explicit linked target, then remove PII, credentials, tokens, customer content, and operational identifiers before commit:
```sh
supabase db dump --data-only --linked > supabase/seed.sql
```
Prove the baseline with `supabase start && supabase db reset` before sharing it.
## Daily workflow
1. Pull team changes.
2. Edit the chosen schema source or create a migration.
3. Generate/review the migration.
4. Replay the complete migration and seed chain with `db reset`.
5. Run database and application tests, including negative authorization cases.
6. Regenerate client types.
7. Commit schema source, migration, tests, and generated types together.
With declarative schemas, `db diff` compares schema files to migration history and ignores ad hoc local Studio/SQL changes. With imperative migrations, it can compare the live local database to migrations. Mixing the mental models silently loses changes.
## Target selection
Pass scope explicitly when a wrong target would be costly. Defaults differ:
- `db diff` and `db reset` default local.
- `db pull`, `db push`, and `db dump` default linked.
Use `supabase projects list`, `supabase migration list`, project configuration, and the intended environment before remote operations.
## Managed deployment
```sh
supabase migration list
supabase db push --dry-run
supabase db push
```
Apply only reviewed migrations. Use `--include-seed` only for explicit non-production environments. `supabase db reset --linked` destroys the linked remote database and is limited to disposable development/staging targets after direct confirmation.
Functions and secrets have separate deployment surfaces:
```sh
supabase functions new FUNCTION_NAME
supabase functions serve
supabase secrets set NAME=value
supabase functions deploy FUNCTION_NAME
supabase functions list
supabase secrets list
supabase config push
```
Use `functions serve` and application tests before deployment. Keep local and platform secrets out of source; `config.toml` should use `env(NAME)` references where supported. `config push`, `functions deploy`, and `secrets set` mutate the linked managed project, so verify the project reference and intended environment first. Read current CLI help and Functions documentation before assuming a flag applies to self-hosted Docker; the platform deploy command and mounted self-hosted Functions workflow are different.
## Managed branches and previews
Managed-platform branches are isolated preview or persistent environments, not Git branches and not a self-hosted Compose feature. Confirm current availability, pricing, and beta status before designing a workflow around them.
```sh
supabase branches list
supabase branches create BRANCH_NAME
```
Branches start without production data. Use reviewed migrations, representative seed data, Functions, configuration, and branch-specific secrets to make a preview reproducible. GitHub integration can create preview branches for pull requests; verify the branch health and migration result rather than treating branch creation as deployment success. Keep branch state such as `.branches/` out of version control.
## CI baseline
A database CI job should pin/setup the CLI, start the local stack, replay migrations, run pgTAP tests, and run application tests against isolated data. A minimal sequence is:
```sh
supabase start
supabase db reset
supabase test db
```
Add type-generation drift checks when generated types are committed. Do not inject production service-role keys into ordinary pull-request jobs. Stop the local stack after the job according to runner lifecycle.
@@ -0,0 +1,130 @@
# Self-hosting deployment
Read this before installing, exposing, or materially reconfiguring the official self-hosted Docker stack.
## Capacity and prerequisites
Current official guidance for all default components:
| Resource | Minimum | Recommended |
|---|---:|---:|
| RAM | 4 GB | 8 GB+ |
| CPU | 2 cores | 4 cores+ |
| Disk | 40 GB SSD | 80 GB+ SSD |
Logs/Analytics are optional and increase resource use. Removing Realtime, Storage, imgproxy, or Functions can reduce requirements, but only do so when the product does not need them and the Compose dependencies are reconciled.
Require Linux administration, Git, Docker Engine, Docker Compose, networking/DNS/firewall knowledge, and a backup/recovery destination. Check architecture support for every pinned image on the actual host.
## Use the official release set
The deployment source is `supabase/supabase/docker`. Prefer the official setup script for a new supported Linux host after inspecting it:
```sh
curl -fsSL https://supabase.link/setup.sh -o setup.sh
less setup.sh
sh setup.sh
```
The script fetches current Docker configuration, generates legacy and asymmetric keys, writes URLs, and pulls images. For non-interactive automation, review its current help and pass explicit values or update the generated `.env` before startup; defaults such as localhost are not production configuration.
Manual installation should copy the complete `docker/` directory, including helper scripts, volume initialization files, overrides, changelog, and tests. Do not copy only `docker-compose.yml`.
## Pre-start gate
Before first start:
1. `docker compose config --quiet` succeeds.
2. Every placeholder password/key is replaced using official generators.
3. `.env` and secret-bearing files are owner-readable only and excluded from version control/backups with inappropriate access.
4. `SUPABASE_PUBLIC_URL`, `API_EXTERNAL_URL` (including `/auth/v1`), `SITE_URL`, and allowed redirects match the intended public topology.
5. Dashboard basic-auth credentials are strong and non-default.
6. Host ports do not conflict and are bound/firewalled intentionally.
7. SMTP and external identity providers are configured if the product depends on them.
8. Persistent database, storage, snippets, functions, and `db-config` locations are included in the backup design.
9. The restore target and rollback path exist before production data arrives.
Use the generated project helper:
```sh
sh run.sh config
sh run.sh compose-config >/dev/null
docker compose config --quiet
sh run.sh start
```
`run.sh start` uses `docker compose up -d --wait`. On a fresh database, Postgres and then Auth, Storage, Realtime, and Supavisor may still be applying first-boot migrations when their initial health retries expire. The helper can exit nonzero while containers continue initializing and later become healthy. Do not restart blindly: inspect logs for forward migration progress, wait for the current initialization to settle, and re-check the complete health set. A persistent failure or migration error needs diagnosis.
## Network and TLS
The default publishes:
- Kong HTTP `8000` and HTTPS `8443`.
- Supavisor session `5432` and transaction `6543`.
For production, place a reverse proxy in front of the API gateway, use a valid certificate, and avoid broad direct database exposure. The official Caddy and Nginx overrides are available:
```sh
sh run.sh config add caddy
# or
sh run.sh config add nginx
sh run.sh start
```
An existing proxy is valid when it:
- Proxies to gateway port `8000`.
- Supports WebSocket upgrades for Realtime.
- Sends correct `X-Forwarded-*` headers.
- Uses public HTTPS values in Supabase/Auth URL variables.
- Does not leave duplicate public gateway bindings when proxy and gateway share a protected Docker network.
Self-signed certificates are development-only; OAuth providers and normal clients require publicly trusted certificates.
## Secrets and keys
Use the official scripts:
```sh
sh utils/generate-keys.sh --update-env
sh utils/add-new-auth-keys.sh --update-env
```
The second script configures asymmetric signing/JWKS plus new opaque API keys while retaining legacy compatibility. Do not partially enable this: Auth signing and every token-verifying service must receive compatible keys.
Changing `JWT_SECRET` requires regenerating JWKS. Rotating opaque API keys requires client/server updates but does not itself invalidate user sessions. Regenerating the asymmetric key pair invalidates sessions signed by the old key and needs a maintenance/communication plan.
Use `utils/db-passwd.sh` for an existing deployment's database password rotation, then recreate services. Do not edit only `.env`; database roles and service configuration must agree.
## Start and smoke-test
```sh
sh run.sh status
sh tests/test-container-logs.sh
sh tests/test-self-hosted.sh http://localhost:8000
```
The official smoke test exercises container health, Studio authentication, Auth lifecycle, REST/GraphQL, Storage including integrity and signed URLs, Realtime, and Functions. Run it from the deployment directory with its `.env`. Treat generated test records as scoped test data; inspect cleanup if the test aborts.
Also verify from outside the host through the real proxy/DNS/TLS boundary. A loopback pass does not prove firewall, certificate, redirect, or WebSocket behavior.
## Optional overrides
Manage official overrides through `run.sh config add/remove`, which updates `COMPOSE_FILE`. Examples include logs, Caddy, Nginx, Envoy, and S3-compatible storage. Always inspect the resolved model after changing overrides:
```sh
sh run.sh config
sh run.sh compose-config
```
Relative Compose files and environment interpolation are part of the deployment contract. A variable in `env_file` is container runtime input; `${VAR}` interpolation in Compose is resolved earlier from the Compose environment/`.env`.
## Production hardening
- Restrict Studio and database administration to trusted networks/users.
- Protect Docker socket access; optional Vector logging reads it.
- Use least-privilege database roles where supported and review Studio/postgres-meta access.
- Rate-limit/monitor public Auth, Storage, REST, Realtime, and Functions routes at the gateway/proxy as appropriate.
- Configure SMTP and abuse controls; self-hosting does not inherit platform protections.
- Back up database data, logical dumps, Storage bytes, Functions/source/config, `.env`/key material securely, and the `db-config` volume's pgsodium root key.
- Alert on external availability, service health, database capacity/connections, backup freshness, and restore-test age.
+56
View File
@@ -0,0 +1,56 @@
# Source index
Research checked: **2026-07-16**.
Use current official documentation and source before making version-sensitive claims. Supabase services are released independently, while the self-hosted Docker directory pins a tested combination.
## Primary documentation
| Topic | Authoritative source |
|---|---|
| Documentation index | https://supabase.com/docs |
| CLI local development | https://supabase.com/docs/guides/local-development/cli/getting-started |
| Local workflow | https://supabase.com/docs/guides/local-development/cli-workflows |
| CLI command reference | https://supabase.com/docs/reference/cli |
| Database migrations | https://supabase.com/docs/guides/local-development/database-migrations |
| Declarative schemas | https://supabase.com/docs/guides/local-development/declarative-database-schemas |
| Seed data | https://supabase.com/docs/guides/local-development/seeding-your-database |
| Database testing | https://supabase.com/docs/guides/local-development/testing/overview |
| Generated types | https://supabase.com/docs/guides/database/api/generating-types |
| Row Level Security | https://supabase.com/docs/guides/database/postgres/row-level-security |
| Edge Functions | https://supabase.com/docs/guides/functions |
| Managed branching | https://supabase.com/docs/guides/deployment/branching |
| GitHub integration | https://supabase.com/docs/guides/deployment/branching/github-integration |
| Self-hosting overview | https://supabase.com/docs/guides/self-hosting |
| Docker deployment | https://supabase.com/docs/guides/self-hosting/docker |
| HTTPS proxy | https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https |
| New API keys/asymmetric auth | https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys |
| Self-hosted Functions | https://supabase.com/docs/guides/self-hosting/self-hosted-functions |
| Postgres 17 upgrade | https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17 |
| Platform-to-self-host restore | https://supabase.com/docs/guides/self-hosting/restore-from-platform |
## Canonical source repositories
- Main repository and official Docker stack: https://github.com/supabase/supabase/tree/master/docker
- Supabase CLI: https://github.com/supabase/cli
- Auth: https://github.com/supabase/auth
- Realtime: https://github.com/supabase/realtime
- Storage: https://github.com/supabase/storage
- Edge Runtime: https://github.com/supabase/edge-runtime
- Supavisor: https://github.com/supabase/supavisor
- Supabase Postgres: https://github.com/supabase/postgres
- PostgREST: https://github.com/PostgREST/postgrest
Within the official Docker directory, read `README.md`, `CONFIG.md`, `CHANGELOG.md`, `versions.md`, `.env.example`, `docker-compose.yml`, `run.sh`, and the relevant `tests/` file together. `CONFIG.md` explicitly distinguishes source-derived facts from interpretive descriptions; use each service's own documentation or source when intent matters.
## Freshness rules
1. Prefer current source and dated changelog entries over older prose examples.
2. Treat exact image tags, CLI flags, environment variables, routes, defaults, and platform parity as version-sensitive.
3. Do not assume the CLI local stack and self-hosted Compose stack run identical versions or expose identical management features.
4. When official pages disagree, reconcile against the current Docker Compose file, Docker changelog, and service source. For example, an older restore page may mention Postgres 15 while the current Docker release and upgrade guide establish Postgres 17 as the default.
5. Record the source URL and access date for operational decisions that will outlive the session.
## Live validation performed
The skill was refined against the official self-hosted Docker configuration from the `supabase/supabase` default branch at commit `ae957414b46527249c22bd9b81f0338bd0956a05` and the stable Supabase CLI `2.109.1` available on 2026-07-16. Validation covered configuration resolution, Linux Docker deployment, service health, the official 35-check end-to-end self-hosted smoke suite, and the current CLI command/help surface. This is execution evidence for that release set, not a guarantee for future images or every infrastructure topology.
+100
View File
@@ -0,0 +1,100 @@
# Troubleshooting
Read this when the local CLI stack or self-hosted deployment fails. Diagnose one layer at a time; do not restart or delete state before reading current evidence.
## Layered sequence
1. **Recent change:** inspect the diff in application migrations, `config.toml`, Compose, `.env.example` merge, gateway templates, image tags, proxy, DNS, and certificates.
2. **Resolved model:** `supabase --version`, `docker compose config --quiet`, active `COMPOSE_FILE`, services, ports, volumes, and interpolated non-secret settings.
3. **Container state:** `docker compose ps --format json`; inspect exit, health, restart count, and OOM state.
4. **Bounded logs:** `docker compose logs --tail=100 SERVICE`; use the official `tests/test-container-logs.sh` for the self-hosted stack.
5. **Internal health:** database, gateway, and failing service from inside the Compose network.
6. **Gateway route:** use the expected API key role and inspect status plus body.
7. **External boundary:** DNS, TLS, proxy headers, WebSocket upgrade, firewall, and callback URLs.
8. **Application semantics:** migration history, grants, RLS, user claims, Storage policy, Realtime publication, and Function code.
After two genuinely different approaches fail, report the evidence and ask before escalating to destructive recovery or security-boundary changes.
## CLI local stack
### `supabase start` fails
- Confirm Docker-compatible runtime is running and accessible.
- Check host RAM allocation and port conflicts.
- Verify the project was initialized and the command runs from the correct root.
- Read the failing container logs and CLI version before upgrading/restarting.
- Preserve uncommitted schema/data before `supabase stop --no-backup`.
### `db diff` reports no changes
If declarative schemas are configured, `db diff` reads `supabase/schemas/`, not ad hoc Studio/SQL changes. Apply the change to schema files. If the project uses imperative migrations, confirm the local database differs from migration history and pass scope explicitly.
### `db reset` fails
The first failing migration and SQL error are the root evidence. Fix the migration or ordering/dependency issue and replay from scratch. Do not patch only the resulting database; the migration chain is the deliverable.
### Local/remote migration mismatch
Run `supabase migration list`. Determine whether SQL is missing, remote schema drifted, or history alone is wrong. Use `db pull` for real schema drift. Use `migration repair` only after proving the actual SQL state; bookkeeping changes can hide defects.
## Self-hosted containers
### Service remains `created`, unhealthy, or restarting
On first boot, Postgres and service-owned migrations can outlast the initial healthcheck retry window. `run.sh start` may exit nonzero even though containers are still making forward progress and later become healthy. Read the migration logs and re-check health before restarting; distinguish slow initialization from a persistent error.
- Run `docker compose config --quiet` and `ps` first.
- Read its logs and dependency health; `depends_on` readiness works only where the official health conditions are present.
- Check database credentials, URL values, and mounted file permissions.
- Check host memory/OOM and disk/inode pressure.
- For Kong entrypoint errors, verify LF line endings; CRLF checkouts break shell entrypoints.
- For rootless Docker/Podman logging, verify `DOCKER_SOCKET_LOCATION` and current Compose interpolation support.
Do not delete volumes to clear a health failure.
### Auth/OAuth links point to the wrong place
Current self-hosted `API_EXTERNAL_URL` includes `/auth/v1`. Confirm `SUPABASE_PUBLIC_URL`, `API_EXTERNAL_URL`, `SITE_URL`, additional redirects, proxy scheme/host, and provider-console callback. Recreate services after environment changes.
### REST root returns `403` with public key
Current self-hosted gateway restricts `/rest/v1/` OpenAPI root to administrative keys. Test an intended table route with the publishable key and expected RLS role before diagnosing PostgREST as unavailable.
### RLS returns no rows or permits too much
- Confirm RLS is enabled on the exact table.
- Inspect grants and policies for the current role/operation.
- Test `auth.uid()`/claims and anonymous `null` behavior.
- Check `USING` versus `WITH CHECK`.
- Ensure the test is not using service role.
- Re-query after writes; an unauthorized update can be a zero-row no-op.
### Realtime connects but emits no changes
Check table publication, RLS visibility, user session/key, committed database change, Realtime logs, and proxy WebSocket forwarding. An HTTP 200 on a route is not subscription proof.
### Storage metadata works but objects are missing
Database restore recovers metadata/policies, not object bytes. Check the filesystem/S3 backend, `GLOBAL_S3_BUCKET`, credentials, endpoint/path style, volume mount, and separate object restore. Verify object checksum through the public/signed path.
### Functions ignore code or environment changes
Restart Functions for mounted code changes. Recreate for environment or secret changes. Verify the mounted path, gateway invocation, global JWT-verification setting, and Function logs.
## Upgrade and recovery failures
### Postgres 17 will not start
Never attach Postgres 17 to an old Postgres 15 data directory. For an actual upgrade, use the official script and preserved rollback data. A leftover `db-config` volume can break a genuinely fresh deployment, but deleting it also deletes custom config and the pgsodium key; only remove it after proving there is no data/Vault recovery value.
### Upgrade runs out of space
Stop and preserve state. The Postgres upgrade requires the source, copied destination, cached binaries, and staging space. Roll back, add capacity or move `TMPDIR`, revalidate `2x database size + 5 GB`, then retry.
### Restore fails across versions
Inventory Postgres version, extensions, service-internal tables/columns, and unsupported settings. Use `supabase db dump` rather than raw `pg_dump` for Supabase-aware filtering. A diagnostic non-transactional attempt may reveal all mismatches, but the final accepted restore must use error-stop and transactional behavior, followed by service-level checks.
## Evidence to report
Report target/environment, CLI or Docker release/commit, exact failing layer, command and exit status, redacted status/log excerpts, recent relevant change, what was verified, and the safest next step. Never include `.env`, keys, passwords, JWTs, email-provider secrets, or complete credential-bearing URLs.