supabase dump & restore guide (runbook for after spend cap removed)

Covers:
- Connection test (direct vs pooler, IPv4 vs IPv6)
- pg_dump commands (schema-only and data-only)
- Restore steps to local DB
- Verification queries
- Schema-per-brand restructure (future work)
This commit is contained in:
2026-06-05 16:45:37 +00:00
parent a266203c07
commit b433c79677
+176
View File
@@ -0,0 +1,176 @@
# Supabase Dump & Restore Guide
This guide is the runbook for when your brother removes the Supabase
spend cap and we can finally connect to the live Supabase project to
pull the real schema + data.
## Prerequisites
- Brother has removed the Supabase spend cap (project status: ACTIVE_HEALTHY)
- Supabase project ref: `wnzkhezyhnfzhkhiflrp` (from CLAUDE.md)
- Supabase DB password: `YLKzP9jz2yqop7jr` (user-provided)
- Local Postgres running on `127.0.0.1:5432` (user: `routecommerce`,
password: `routecommerce_dev_password`, db: `route_commerce`)
## Connection Test
The Supabase hostname has NO IPv4 address from this dev box. You
must use the **Supabase Supavisor pooler** (port 6543) which resolves
over IPv4. From your home network, you can also use the direct
hostname on port 5432.
```bash
# Direct hostname (home network only — dev box has no IPv6)
psql "postgres://postgres.wnzkhezyhnfzhkhiflrp:YLKzP9jz2yqop7jr@db.wnzkhezyhnfzhkhiflrp.supabase.co:5432/postgres"
# Pooler (works from dev box — IPv4 only)
psql "postgres://postgres.wnzkhezyhnfzhkhiflrp:YLKzP9jz2yqop7jr@aws-0-us-east-1.pooler.supabase.com:6543/postgres"
```
If neither works after the cap is removed, check:
1. `getent hosts db.wnzkhezyhnfzhkhiflrp.supabase.co` — should return IPv4
2. Supabase dashboard → Settings → API → "Direct connection" string
## Capture Schema (no data)
```bash
# From your home network, with the password set:
PGPASSWORD="YLKzP9jz2yqop7jr" pg_dump \
--host=db.wnzkhezyhnfzhkhiflrp.supabase.co \
--port=5432 \
--username=postgres \
--dbname=postgres \
--schema-only \
--no-owner \
--no-privileges \
--no-acl \
--file=supabase/captured_schema.sql
```
This produces a SQL file with all CREATE TABLE, CREATE FUNCTION,
CREATE INDEX, etc. statements. May be 50-200 MB depending on the
function count. **Compress it before committing:**
```bash
gzip supabase/captured_schema.sql # → captured_schema.sql.gz
```
## Capture Data (no schema)
```bash
PGPASSWORD="YLKzP9jz2yqop7jr" pg_dump \
--host=db.wnzkhezyhnfzhkhiflrp.supabase.co \
--port=5432 \
--username=postgres \
--dbname=postgres \
--data-only \
--no-owner \
--no-privileges \
--no-acl \
--disable-triggers \
--file=supabase/captured_data.sql
```
Use `--disable-triggers` so the dump skips triggers that might fire
on INSERT and slow things down. Compress:
```bash
gzip supabase/captured_data.sql
```
## Restore to Local DB
```bash
cd /path/to/route-commerce
export PGPASSWORD=routecommerce_dev_password
# 1. Wipe the synthesized schema
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
-c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
# 2. Apply the real schema
zcat supabase/captured_schema.sql.gz | \
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
-v ON_ERROR_STOP=1 2>&1 | tee /tmp/schema_restore.log
# 3. Apply the data
zcat supabase/captured_data.sql.gz | \
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
-v ON_ERROR_STOP=1 2>&1 | tee /tmp/data_restore.log
```
## Verify
```bash
# Should show ~70+ tables (not just the 70 from the synthesized schema)
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "\dt"
# Should show actual data, not 0 rows everywhere
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM brands;"
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM products;"
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM orders;"
```
## Clean Up
```bash
# Once verified, drop the synthesized schema (no longer needed)
rm supabase/synthesized/000_base_schema.sql
git add -A
git commit -m "remove synthesized schema (real dump now in place)"
```
## Schema-Per-Brand Restructure (FUTURE, not in this dump)
The user wants each brand in its own Postgres schema:
- `brand_tuxedo` — products, orders, stops, etc. for Tuxedo
- `brand_indian_river_direct` — products, orders, stops, etc. for IRD
This is a major refactor and is **NOT** part of the dump-and-restore.
Plan for it as a follow-up phase once the data is loaded into shared
tables and the app is verified working.
The refactor will involve:
1. Creating schemas `brand_tuxedo`, `brand_indian_river_direct`
2. Moving brand-specific tables into the appropriate schema
3. Updating all RPC SECURITY DEFINER functions to set
`search_path` based on caller brand
4. Updating server actions to set `search_path` per request
5. Updating PostgREST config to expose schemas
6. Testing brand isolation thoroughly
## Troubleshooting
### "password authentication failed"
Password has been rotated. Check Supabase dashboard → Settings →
Database → Reset password. The user-provided password
`YLKzP9jz2yqop7jr` may need to be reset.
### "could not translate host name"
Dev box has no IPv6. Use the pooler hostname
`aws-0-us-east-1.pooler.supabase.com` or run from a machine with IPv6.
### "permission denied for schema auth"
The dump includes the `auth` schema which is Supabase-managed.
Filter it out with: `--exclude-schema=auth --exclude-schema=storage
--exclude-schema=realtime --exclude-schema=supabase_functions`.
### "relation already exists"
You forgot step 1 (DROP SCHEMA public CASCADE). The data restore
runs `INSERT` and may not recreate tables — only the schema dump
does that.
### "out of memory" during data restore
The data file is huge. Use `--single-transaction` to keep
Postgres from spilling to disk:
```bash
zcat supabase/captured_data.sql.gz | \
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
--single-transaction \
-v ON_ERROR_STOP=0 2>&1 | tee /tmp/data_restore.log
```
### Data dump is too big to commit
Don't commit it. The data file is in `.gitignore` and gets
re-loaded on first run via a one-shot script.