mirror of
https://github.com/supabase/supabase.git
synced 2026-05-19 11:30:47 +08:00
187 lines
9.1 KiB
Plaintext
187 lines
9.1 KiB
Plaintext
---
|
|
title: 'Restore a Platform Project to Self-Hosted'
|
|
description: 'Restore your database from the Supabase platform to a self-hosted instance.'
|
|
subtitle: 'Restore your database from the Supabase platform to a self-hosted instance.'
|
|
---
|
|
|
|
This guide walks you through restoring your database from a Supabase platform project to a [self-hosted Docker instance](/docs/guides/self-hosting/docker). Transferring storage objects or redeploying edge functions is not covered here.
|
|
|
|
## Before you begin
|
|
|
|
You need:
|
|
|
|
- A new self-hosted Supabase instance ([Docker setup guide](/docs/guides/self-hosting/docker))
|
|
- [Supabase CLI](/docs/guides/local-development/cli/getting-started) installed (or use `npx supabase`)
|
|
- [Docker Desktop](https://docs.docker.com/get-started/get-docker/) installed (required by the CLI)
|
|
- `psql` installed ([official installation guide](https://www.postgresql.org/download/))
|
|
- Your Supabase database passwords (for platform and self-hosted)
|
|
|
|
## Step 1: Get your platform connection string
|
|
|
|
On your managed Supabase project dashboard, click [**Connect**](/dashboard/project/_?showConnect=true) 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:
|
|
|
|
```sh
|
|
supabase db dump --db-url "[CONNECTION_STRING]" -f roles.sql --role-only
|
|
```
|
|
|
|
```sh
|
|
supabase db dump --db-url "[CONNECTION_STRING]" -f schema.sql
|
|
```
|
|
|
|
```sh
|
|
supabase db dump --db-url "[CONNECTION_STRING]" -f data.sql --use-copy --data-only
|
|
```
|
|
|
|
This produces SQL files that are compatible across Postgres versions.
|
|
|
|
<Admonition type="note">
|
|
|
|
Using `supabase db dump` executes `pg_dump` under the hood but applies Supabase-specific filtering - it excludes internal schemas, strips reserved roles, and adds idempotent `IF NOT EXISTS` clauses. Using raw `pg_dump` directly will include Supabase internals and cause permission errors during restore. CLI requires Docker because it runs `pg_dump` inside a container from the Supabase Postgres image rather than requiring a local Postgres installation.
|
|
|
|
</Admonition>
|
|
|
|
## Step 3: Prepare your self-hosted instance
|
|
|
|
Before restoring, check the following on your self-hosted instance:
|
|
|
|
- **Extensions**: Enable any non-default extensions your Supabase 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](/docs/guides/self-hosting/docker#accessing-postgres) connection string for self-hosted Supabase 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 Supabase on a VPS, or locally.
|
|
|
|
Run `psql` to restore:
|
|
|
|
```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 "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:
|
|
|
|
```sh
|
|
psql "postgres://postgres.your-tenant-id:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres"
|
|
```
|
|
|
|
```sql
|
|
-- 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 `.env` file. Set the relevant `GOTRUE_EXTERNAL_*` variables. See the Auth repository [README](https://github.com/supabase/auth) 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 Supabase 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.
|
|
|
|
<Admonition type="tip">
|
|
|
|
If your managed project runs Postgres 17, consider starting your self-hosted deployment with Postgres 17 as well. See the [Postgres 17 guide](/docs/guides/self-hosting/postgres-upgrade-17) for setup instructions.
|
|
|
|
</Admonition>
|
|
|
|
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](#version-mismatches-between-platform-and-self-hosted) 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 15
|
|
- `COPY` statements for tables that don't exist on self-hosted (e.g., `auth.oauth_clients`, `storage.buckets_vectors`, `storage.vector_indexes`)
|
|
- `COPY` statements with columns added in newer Auth versions (e.g., `auth.flow_state` with `oauth_client_state_id`, `linking_target_id`)
|
|
|
|
**Workaround:** Edit `data.sql` before restoring:
|
|
|
|
```sh
|
|
# 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](https://github.com/supabase/supabase/blob/master/docker/CHANGELOG.md) 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:
|
|
|
|
```sql
|
|
select * from pg_available_extensions;
|
|
```
|
|
|
|
### Connection refused
|
|
|
|
Make sure your self-hosted Postgres port is accessible. In the default [self-hosted Supabase](/docs/guides/self-hosting/docker#accessing-postgres) setup, the user is `postgres.your-tenant-id` with Supavisor on port `5432`.
|
|
|
|
### Legacy Studio configuration
|
|
|
|
Studio in self-hosted Supabase 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](https://github.com/supabase/supabase/blob/2cb5befaa377a42b6d6ca152b98105b59054f2f4/docker/docker-compose.yml#L30) 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:
|
|
|
|
```sql
|
|
ALTER ROLE your_custom_role WITH PASSWORD 'new-password';
|
|
```
|
|
|
|
### Additional resources
|
|
|
|
- [Backup and Restore using the CLI](/docs/guides/platform/migrating-within-supabase/backup-restore)
|
|
- [Restore Dashboard backup](/docs/guides/platform/migrating-within-supabase/dashboard-restore)
|