This guide walks you through restoring your database from a Zuvo platform project to a self-hosted Docker instance. Transferring storage objects or redeploying edge functions is not covered here.
Before you begin
You need:
- A new self-hosted Zuvo instance (Docker setup guide)
- Zuvo CLI installed (or use
npx supabase) - Docker Desktop installed (required by the CLI)
psqlinstalled (official installation guide)- Your Zuvo database passwords (for platform and self-hosted)
Step 1: Get your platform connection string
On your managed Zuvo project dashboard, click Connect and copy the connection string (use the session pooler or direct connection).
Step 2: Back up your platform database
Export roles, schema, and data as three separate SQL files:
supabase db dump --db-url "[CONNECTION_STRING]" -f roles.sql --role-only
supabase db dump --db-url "[CONNECTION_STRING]" -f schema.sql
supabase db dump --db-url "[CONNECTION_STRING]" -f data.sql --use-copy --data-only
This produces SQL files that are compatible across Postgres versions.
Step 3: Prepare your self-hosted instance
Before restoring, check the following on your self-hosted instance:
- Extensions: Enable any non-default extensions your Zuvo project uses. You can check which extensions are active by querying
select * from pg_extension;on your managed database (or check Database Extensions in Dashboard).
Step 4: Restore to your self-hosted database
Connect to your self-hosted Postgres and restore the dump files. The default connection string for self-hosted Zuvo is:
postgres://postgres.your-tenant-id:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres
Where [POSTGRES_PASSWORD] is the value of POSTGRES_PASSWORD in your self-hosted .env file.
Use your domain name, your server IP, or localhost for [your-domain] depending on whether you are running self-hosted Zuvo on a VPS, or locally.
Run psql to restore:
psql \
--single-transaction \
--variable ON_ERROR_STOP=1 \
--file roles.sql \
--file schema.sql \
--command 'SET session_replication_role = replica' \
--file data.sql \
--dbname "postgres://postgres.your-tenant-id:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres"
Setting session_replication_role to replica disables triggers during the data import, preventing issues like double-encryption of columns.
Step 5: Verify the restore
Connect to your self-hosted database and run a few checks:
psql "postgres://postgres.your-tenant-id:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres"
-- Check your tables are present
\dt public.*
-- Verify row counts on key tables
SELECT count(*) FROM auth.users;
-- Check extensions
SELECT * FROM pg_extension;
What's included in the restore and what's not
The database dump includes your schema, data, roles, RLS policies, database functions, triggers, and auth.users. However, several things require separate configuration on your self-hosted instance:
| Requires manual setup | How to configure |
|---|---|
| JWT secrets and API keys | Generate new ones and update .env |
| Auth provider settings (OAuth, Apple, etc.) | Configure GOTRUE_EXTERNAL_* variables in .env |
| Edge functions | Manually copy to your self-hosted instance |
| Storage objects | Transfer separately (not covered in this guide) |
| SMTP / email settings | Configure SMTP_* variables in .env |
| Custom domains and DNS | Point your DNS to the self-hosted server |
Auth considerations
Your auth.users table and related data are included in the database dump, so user accounts are preserved. However:
- JWT secrets differ between your platform and self-hosted instances. Existing tokens issued by the platform project will not be valid. Users will need to re-authenticate.
- Social auth providers (Apple, Google, GitHub, etc.) need to be configured in your self-hosted
.envfile. Set the relevantGOTRUE_EXTERNAL_*variables. See the Auth repository README for all available options. - Redirect URLs in your OAuth provider consoles (Apple Developer, Google Cloud Console, etc.) must be updated to point to your self-hosted hostname instead of
*.supabase.co.
Postgres version compatibility
Managed Zuvo may run a newer Postgres version (Postgres 17) than the self-hosted Docker image (currently it's Postgres 15 by default). The supabase db dump command produces plain SQL files that work across major Postgres versions.
Keep in mind:
- The data dump may include Postgres 17-only settings or reference tables/columns from newer Auth and Storage versions that don't exist on self-hosted yet. See Version mismatches in the troubleshooting section.
- Run the restore on a test self-hosted instance first to identify any incompatibilities.
- Check that all extensions you use are available on the self-hosted Postgres version.
Troubleshooting
Version mismatches between platform and self-hosted
The platform may run a newer Postgres version (17 vs 15) and newer Auth service versions than self-hosted. The data dump can contain settings, tables, or columns that don't exist on your new self-hosted instance.
Common issues in data.sql:
SET transaction_timeout = 0- a Postgres 17-only setting that fails on Postgres 15COPYstatements for tables that don't exist on self-hosted (e.g.,auth.oauth_clients,storage.buckets_vectors,storage.vector_indexes)COPYstatements with columns added in newer Auth versions (e.g.,auth.flow_statewithoauth_client_state_id,linking_target_id)
Workaround: Edit data.sql before restoring:
# Comment out PG17-only transaction_timeout
sed -i 's/^SET transaction_timeout/-- &/' data.sql
For missing tables or column mismatches, comment out the relevant COPY ... FROM stdin; line and its corresponding \. terminator. Run the restore without --single-transaction first to identify all failures, then fix them and run the final restore with --single-transaction.
Keeping your self-hosted configuration up to date will minimize these gaps.
Extension not available
If the restore fails because an extension isn't available, check whether it's supported on your self-hosted Postgres version. You can list available extensions with:
select * from pg_available_extensions;
Connection refused
Make sure your self-hosted Postgres port is accessible. In the default self-hosted Zuvo setup, the user is postgres.your-tenant-id with Supavisor on port 5432.
Legacy Studio configuration
Studio in self-hosted Zuvo historically used supabase_admin role (superuser) instead of postgres. Objects created via Studio UI were owned by supabase_admin. Check your docker-compose.yml configuration to see if POSTGRES_USER_READ_WRITE is set to postgres.
Custom roles missing passwords
If you created custom database roles with the LOGIN attribute on your platform project, their passwords are not included in the dump. Set them manually after restore:
ALTER ROLE your_custom_role WITH PASSWORD 'new-password';