Migrating within Supabase
Migrate from one Supabase project to another
Migrating projects can be achieved using the Supabase CLI. This is particularly useful for older projects (for example to use a newer Postgres version).
Before you begin
- Install Postgres so you can run
psql
andpg_dump
. - Install the latest version of Supabase CLI.
- Create a new Supabase project.
- Install Docker Desktop for your platform.
- Set environment variables for the old project's database URL as
$OLD_DB_URL
and the new project's as$NEW_DB_URL
. To find the database URL for a project, go to the project's dashboard page Project Settings/Database and look underConnection string
. If your network provider supports IPv6, you can disableUse connection pooling
. Otherwise, enableUse connection pooling
. For the pooler mode,Transaction
will work.
Backup your old database
- Run the following command from your terminal:
_10supabase db dump --db-url "$OLD_DB_URL" -f roles.sql --role-only_10supabase db dump --db-url "$OLD_DB_URL" -f schema.sql_10supabase db dump --db-url "$OLD_DB_URL" -f data.sql --use-copy --data-only
Restore to your new project
In your new project:
- Enable Database Webhooks if you enabled them in your old project.
- Enable any extensions that were enabled in your old project.
If you use column encryption, first copy the root encryption key to your new project using your Personal Access Token.
_10export OLD_PROJECT_REF="<old_project_ref>"_10export NEW_PROJECT_REF="<new_project_ref>"_10export SUPABASE_ACCESS_TOKEN="<personal_access_token>"_10_10curl "https://api.supabase.com/v1/projects/$OLD_PROJECT_REF/pgsodium" \_10 -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" |_10curl "https://api.supabase.com/v1/projects/$NEW_PROJECT_REF/pgsodium" \_10 -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \_10 -X PUT --json @-
Then run the following command from your terminal:
_10psql \_10 --single-transaction \_10 --variable ON_ERROR_STOP=1 \_10 --file roles.sql \_10 --file schema.sql \_10 --command 'SET session_replication_role = replica' \_10 --file data.sql \_10 --dbname "$NEW_DB_URL"
Setting the session_replication_role
to replica
disables all triggers so that columns are not double encrypted.
Troubleshooting notes:
- If you have created any custom roles with
login
attribute, you have to manually set their passwords in the new project. - If you run into any permission errors related to
supabase_admin
during restore, edit theschema.sql
file and comment out any lines containingALTER ... OWNER TO "supabase_admin"
.
Preserving migration history
If you were using Supabase CLI for managing migrations on your old database and would like to preserve the migration history in your newly restored project, you need to insert the migration records separately using the following commands.
_10supabase db dump --db-url "$OLD_DB_URL" -f history_schema.sql --schema supabase_migrations_10supabase db dump --db-url "$OLD_DB_URL" -f history_data.sql --use-copy --data-only --schema supabase_migrations_10psql \_10 --single-transaction \_10 --variable ON_ERROR_STOP=1 \_10 --file history_schema.sql \_10 --file history_data.sql \_10 --dbname "$NEW_DB_URL"
Schema changes to auth
and storage
If you have modified the auth
and storage
schemas in your old project, such as adding triggers or RLS policies, you have to restore them separately. The Supabase CLI can help you diff the changes to these schemas using the following commands.
_10supabase link --project-ref "$OLD_PROJECT_REF"_10supabase db diff --linked --schema auth,storage > changes.sql
Enable publication on tables
Replication for Realtime is disabled for all tables in your new project. On the Publications page in the Dashboard, select your new project and enable replication for tables that were enabled in your old project.
Migrate storage objects
The new project has the old project's Storage buckets, but the Storage objects need to be migrated manually. Use this script to move storage objects from one project to another.
_52// npm install @supabase/supabase-js@1_52const { createClient } = require('@supabase/supabase-js')_52_52const OLD_PROJECT_URL = 'https://xxx.supabase.co'_52const OLD_PROJECT_SERVICE_KEY = 'old-project-service-key-xxx'_52_52const NEW_PROJECT_URL = 'https://yyy.supabase.co'_52const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy'_52_52;(async () => {_52 const oldSupabaseRestClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY, {_52 db: {_52 schema: 'storage',_52 },_52 })_52 const oldSupabaseClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY)_52 const newSupabaseClient = createClient(NEW_PROJECT_URL, NEW_PROJECT_SERVICE_KEY)_52_52 // make sure you update max_rows in postgrest settings if you have a lot of objects_52 // or paginate here_52 const { data: oldObjects, error } = await oldSupabaseRestClient.from('objects').select()_52 if (error) {_52 console.log('error getting objects from old bucket')_52 throw error_52 }_52_52 for (const objectData of oldObjects) {_52 console.log(`moving ${objectData.id}`)_52 try {_52 const { data, error: downloadObjectError } = await oldSupabaseClient.storage_52 .from(objectData.bucket_id)_52 .download(objectData.name)_52 if (downloadObjectError) {_52 throw downloadObjectError_52 }_52_52 const { _, error: uploadObjectError } = await newSupabaseClient.storage_52 .from(objectData.bucket_id)_52 .upload(objectData.name, data, {_52 upsert: true,_52 contentType: objectData.metadata.mimetype,_52 cacheControl: objectData.metadata.cacheControl,_52 })_52 if (uploadObjectError) {_52 throw uploadObjectError_52 }_52 } catch (err) {_52 console.log('error moving ', objectData)_52 console.log(err)_52 }_52 }_52})()
Transfer to a different organization
Note that project migration is for transferring your projects to different regions. If you need to move your project to a different organization without touching the infrastructure, see project transfers.