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:
@@ -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.
|
||||
Reference in New Issue
Block a user