This document describes how to use the Supabase Migration Tool to migrate data from Supabase Cloud to Alibaba Cloud RDS Supabase or between RDS Supabase instances. The tool provides both CLI and Web UI modes.
Background information
The Supabase Migration Tool supports the following migration scenarios:
-
Migrate from Supabase Cloud to Alibaba Cloud RDS Supabase (cloud-to-cloud migration)
-
Migrate between Alibaba Cloud RDS Supabase instances (both source and target are Alibaba Cloud RDS Supabase)
The tool provides two modes:
-
CLI mode: Run migrations directly from the terminal. Suitable for CI/CD pipelines, Kubernetes Jobs, and script automation.
-
Web UI mode: Provides a visual configuration interface, one-click migration, and real-time progress logs.
Migration covers four core data assets: user authentication data, business data, object storage files, and Edge Functions.
Features
-
Web UI: Provides a visual interface for configuration, one-click migration, and real-time progress and log monitoring.
-
CLI mode: Run migrations directly from the terminal. Suitable for CI/CD pipelines, Kubernetes Jobs, and script automation.
-
Selective migration: Specify which schemas, buckets, and functions to migrate, avoiding the need for a full migration.
-
Pre-restore cleanup: An option to clean the target destination before restoring data helps prevent conflicts with existing objects.
-
MIME type preservation: The tool automatically retains the correct MIME Content-Type for files during Storage migration, allowing them to be previewed online immediately after transfer.
-
Flexible deployment for Edge Functions: Supports both CLI and API deployment methods. The API method is recommended when migrating to an RDS Supabase instance.
-
Environment variable overrides: Override configuration settings with environment variables prefixed with
MIGRATOR_, simplifying credential management in containerized deployments. -
Version compatibility check: Automatically checks
pg_dumpcompatibility with the source and target database server versions to prevent version-mismatch failures.
Migration pipelines
The tool supports the following four migration pipelines, which are executed in a fixed order:
|
Order |
Pipeline |
Description |
|
1 |
Auth |
Migrates user authentication data, including |
|
2 |
Database |
Uses |
|
3 |
Storage |
Streams files from all source buckets using the Supabase Storage REST API and uploads them to the target. It also preserves the original MIME Content-Type of each file. |
|
4 |
Edge Functions |
Downloads the eszip bundle for each source function and deploys it to the target using either the API or the Supabase CLI. |
The Auth pipeline must run before the Database pipeline because business tables often contain foreign keys that reference auth.users. Restoring business tables first would cause the migration to fail due to foreign key constraints.
Auth user data migration
This pipeline migrates only the data from the auth tables, not the DDL (Data Definition Language). The target GoTrue service automatically initializes and manages the auth schema.
-
By default, migrates
auth.usersandauth.identities. -
Optionally includes
auth.mfa_factors. -
Uses
SET session_replication_role = replicato bypass triggers and constraint checks, which speeds up bulk inserts. -
Handles duplicate key conflicts as warnings without interrupting the migration.
Database migration
Uses pg_dump and pg_restore to perform a full migration of the database schema and data.
-
Supports both
custom(recommended) andplaindump formats. -
You can specify particular schemas, such as migrating only the
publicschema, instead of migrating the entire database. -
You can optionally clean the target destination by using
DROP SCHEMA CASCADEandCREATEbefore the restore to prevent conflicts with existing objects. -
Optionally includes roles.
Storage file migration
Replicates buckets and files by using the Supabase Storage REST API.
-
Automatically creates buckets that do not exist on the target. The tool does not modify existing buckets or their properties.
-
Streams file content, enabling the migration of large files without consuming excessive memory.
-
Concurrency control: Uses 5 parallel workers by default for file transfers.
-
Preserves the correct
Content-Type, allowing files to be previewed online immediately after migration. -
Uploads use the upsert strategy (
x-upsert: true). Re-running the migration overwrites files with the same name on the target. -
Object-level migration: In addition to filtering by bucket, you can specify exact objects to migrate. This is useful for retrying failed files or migrating a subset. After configuring
storage.objects(a list ofbucket/pathentries, supporting multi-level directory paths), the tool skips the full scan and migrates only the listed objects. This setting takes priority overbuckets(ignoring bucket filters). For details, see Migrate by object list.
Migrate by object list
For scenarios such as retrying failed files or migrating only a few files, you can specify exact objects to migrate and skip the full scan. The tool provides the following three entry points. Choose any one:
-
Command-line inline (comma-separated): Suitable for ad-hoc migration of a small number of objects.
rds-supabase-migrator migrate storage -c config/rds-to-rds.yaml \ --objects "course-videos/test-course/Lesson1.mp4,student-answers/uuid/file.png" -
Read from file: Suitable for a large number of objects. One
bucket/pathper line. Lines starting with#and empty lines are automatically ignored.rds-supabase-migrator migrate storage -c config/rds-to-rds.yaml --objects-file objects.txt -
Set
storage.objectsin the configuration file. This applies globally to both CLI and Web UI modes. You can also fill in the objects list in the Storage configuration area of the Web UI.migration: storage: enabled: true objects: - "course-videos/test-course/Lesson1.mp4" - "student-answers/uuid/file.png"
For temporary or one-off failure retries, you can also use the built-in scripts/retry_storage.py script. For regular scenarios, use the built-in capabilities described above.
Edge Functions deployment
Supports two deployment methods. The API method is recommended when migrating to an RDS Supabase instance.
-
API method (Recommended): Uploads the function bundle as multipart/form-data using the
POST /functions/v1/manage/deploy?slug={slug}endpoint. -
CLI method: Deploys functions by calling the
supabase functions deploycommand.
Prerequisites
-
You have created an RDS Supabase project that is in the Running state. For instructions, see Create an RDS Supabase project.
-
You have added the egress IP of the migration tool's host to the IP whitelists for the following services:
-
The IP whitelist of the RDS PostgreSQL instance managed by your RDS Supabase project. This allows direct connections for
pg_restore. For instructions, see Configure an IP whitelist. -
The IP whitelist for your RDS Supabase instance. Add the IP address in the Whitelist Information section on the details page of your RDS Supabase project.
-
You must also add the egress IP of the migration tool to your source Supabase project's IP whitelist.
NoteRDS-to-RDS migration: For migrations between RDS Supabase instances, both the source and target PostgreSQL instances managed by RDS Supabase must have public endpoints enabled. Add the migration tool's egress IP to the whitelists of both the source and target instances.
-
-
Python 3.10 or later is installed on the machine where you will run the migration tool.
-
PostgreSQL client tools (
pg_dump,pg_restore, andpsql) are installed on the machine. The client version must be greater than or equal to the higher of the source and target PostgreSQL server versions.
Important notes
-
Default database name: The default database for an RDS Supabase instance is
supabase_db. You must use this database name in the target connection string, for example,postgresql://postgres:PASSWORD@pgm-xxx.pg.rds.aliyuncs.com:5432/supabase_db. If you connect to thepostgresdatabase by mistake, your business tables will be restored to the wrong database. -
Use the cleanup option with caution: If you enable the
clean_target_before_restoreoption, the tool executesDROP SCHEMA ... CASCADEbefore the restore process begins. All objects and data within the specified schemas on the target are permanently deleted and cannot be recovered. You can enable this option for the initial migration to an empty instance, but ensure it is disabled if the target already contains business data. -
Fixed pipeline order: The migration pipeline order cannot be changed. The Auth pipeline must run before the Database pipeline. Otherwise, foreign key constraints in business tables referencing the
auth.userstable will cause the Database restore to fail. -
Storage migration overwrite strategy: File uploads use upsert mode. Re-running the migration overwrites files with the same name on the target. The tool only creates buckets that do not exist on the target and does not modify existing bucket properties.
-
Auth pipeline migrates data only: The Auth pipeline migrates only data, not Data Definition Language (DDL). The GoTrue service on the target automatically initializes the auth schema, and attempting to re-create it would cause a conflict.
-
API deployment for Edge Functions: When deploying Edge Functions using the API method, the
agent-runtimeservice on the target must be running. This service is enabled by default in RDS Supabase. -
RDS-to-RDS migration notes: Both the source and target databases must have public endpoints enabled (configured in the RDS console). Add the migration tool's IP to the whitelists of both instances. For RDS-to-RDS migrations, we recommend enabling
clean_target_before_restoreto avoid conflicts with residual data.
Step 1: Download the source code and configure migration parameters
First, download the migration tool source code package to obtain the configuration file templates. Then, select the appropriate template based on your migration scenario and fill in the source and target connection details and pipeline settings.
Download the project source code
Download the migration tool source code package supabase-migration-master-v1.2.zip and extract it on the machine that will run the tool. The package contains the Docker Compose configuration file (docker-compose.yaml), a Dockerfile, an example configuration file (config/default.yaml.example), and the Python source code. These files are the foundation for all deployment methods.
Configure migration parameters
Cloud-to-RDS migration configuration
Create a configuration file from the example:
cp config/default.yaml.example config/default.yaml
Edit config/default.yaml. The complete example is as follows:
source:
project_ref: "your-project-ref"
database_url: "postgresql://postgres:YOUR_PASSWORD@db.xxx.supabase.co:5432/postgres"
service_role_key: "eyJ..."
management_api_token: "sbp_..."
api_base: "https://xxx.supabase.co"
target:
project_url: "http://your-rds-supabase/"
database_url: "postgresql://postgres:YOUR_PASSWORD@pgm-xxx.pg.rds.aliyuncs.com:5432/supabase_db"
service_role_key: "eyJ..."
api_base: "http://your-rds-supabase/"
migration:
auth:
enabled: false # Cloud Auth data is restricted; disabled by default. Enable manually if needed.
database:
enabled: true
pg_bin_path: "/opt/homebrew/opt/postgresql@17/bin" # Local deployment: specify client tools directory. Docker: omit.
clean_target_before_restore: false # Use with caution. This runs DROP SCHEMA CASCADE.
storage:
enabled: true
edge_functions:
enabled: true
deploy_method: "api" # Recommended. More reliable for RDS Supabase.
RDS-to-RDS migration configuration
Create a configuration file from the example:
cp config/rds-to-rds.example.yaml config/rds-to-rds.yaml
Edit config/rds-to-rds.yaml. The key difference is that management_api_token and project_ref are left empty. The complete example is as follows:
source:
project_ref: "" # Leave empty for RDS
database_url: "postgresql://postgres:PASSWORD@source-pgm-xxx:5432/supabase_db"
service_role_key: "eyJ..."
management_api_token: "" # Leave empty to auto-detect RDS mode
api_base: "http://SOURCE_IP/"
target:
database_url: "postgresql://postgres:PASSWORD@target-pgm-xxx:5432/supabase_db"
service_role_key: "eyJ..."
api_base: "http://TARGET_IP/"
migration:
database:
enabled: true
pg_bin_path: "/opt/homebrew/opt/postgresql@17/bin"
schemas: ["public"]
clean_target_before_restore: true # Recommended for RDS-to-RDS migrations
auth:
enabled: true
storage:
enabled: true
edge_functions:
enabled: true
deploy_method: "api"
Source type auto-detection: The tool automatically detects the source type based on whether management_api_token is empty. If non-empty, the tool uses Supabase Cloud mode (calling the api.supabase.com Management API). If empty, the tool uses RDS mode (calling the local /functions/v1/manage API).
If you prefer not to store credentials in a file, you can override configuration settings with environment variables. Use the MIGRATOR_ prefix and double underscores (__) to represent nested levels:
export MIGRATOR_SOURCE__DATABASE_URL="postgresql://..."
export MIGRATOR_SOURCE__SERVICE_ROLE_KEY="eyJ..."
Cloud source credentials
|
Parameter |
Purpose |
How to obtain |
|
|
Identifies the source project. |
The project ID from your Supabase Dashboard URL. This is the |
|
|
Source database connection string (for |
Supabase Dashboard > Settings > Database > Connection string (URI). |
|
|
Source Storage and Auth API authentication. |
Supabase Dashboard > Settings > API > service_role key. |
|
|
Edge Functions management. |
Supabase Dashboard > Account > Access Tokens. The token starts with |
|
|
Source REST API base URL. |
Format: |
|
|
Target project home page URL. |
On the RDS Supabase project details page, find the public endpoint in the Network Information section. |
|
|
Target database connection string (for |
Format: |
|
|
Target Storage, Auth, and Functions API authentication. |
On the RDS Supabase project details page, copy the value of ServiceKey from the API Key section. For more details, see Create an RDS Supabase project. |
|
|
Target API gateway URL. |
The built-in Kong gateway address for your RDS Supabase instance. This is typically the same as |
RDS source credentials
For RDS-to-RDS migrations, the source credentials differ from Cloud source credentials as follows:
|
Parameter |
Purpose |
How to obtain |
|
|
Not required. |
Leave empty. RDS mode does not require project_ref. |
|
|
Direct connection to source for pg_dump. |
Alibaba Cloud console > RDS Supabase details > Connection address (public endpoint, database name: |
|
|
All source API calls. |
Alibaba Cloud console > RDS Supabase details > API Key > ServiceKey. |
|
|
Not required. |
Leave empty. The tool auto-detects RDS mode when this is empty. |
|
|
Source API gateway address. |
Public IP of the source RDS Supabase, for example, |
Target credentials are the same for both Cloud-to-RDS and RDS-to-RDS migration scenarios. They are always the connection details of the target RDS Supabase. See the target section in the table above.
Step 2: Deploy the migration tool
After configuring the parameters in Step 1, choose one of the following deployment methods. Docker deployment is recommended. The Docker image includes Python 3.12 and PostgreSQL 18 clients (backward compatible with PG 17 and PG 15), so you do not need to install dependencies manually.
Method 1: Deploy with Docker Compose (recommended)
-
On the machine where you will run the tool, navigate into the
supabase-migratordirectory. -
Create a configuration file from the example.
cp config/default.yaml.example config/default.yamlEdit
config/default.yamlas described in Step 1: Download the source code and configure migration parameters to specify the source and target connection details. -
Start the migration tool container.
docker compose up -dAfter the container starts, you can access the web UI at
http://<YOUR_MACHINE_IP>:8080.
Method 2: Deploy with Docker run
Use this method if you do not have Docker Compose installed. First, build the image, and then choose a startup method based on whether you want to store credentials on disk.
-
Build the Docker image.
docker build -t supabase-migrator . -
Choose one of the following methods to start the container.
Configuration file
Mount your locally edited
config/default.yamlfile into the container. This method manages credentials as a file on disk.docker run --rm -p 8080:8080 \ -v ./config/default.yaml:/app/config/default.yaml:ro \ supabase-migratorEnvironment variables
Pass configuration as environment variables to avoid storing sensitive credentials on disk. Use the
MIGRATOR_prefix for each variable and double underscores (__) to represent nested levels in the configuration file. For example:docker run --rm -p 8080:8080 \ -e MIGRATOR_SOURCE__PROJECT_REF="your-project-ref" \ -e MIGRATOR_SOURCE__DATABASE_URL="postgresql://postgres:PASSWORD@db.xxx.supabase.co:5432/postgres" \ -e MIGRATOR_SOURCE__SERVICE_ROLE_KEY="eyJ..." \ -e MIGRATOR_SOURCE__MANAGEMENT_API_TOKEN="sbp_..." \ -e MIGRATOR_SOURCE__API_BASE="https://xxx.supabase.co" \ -e MIGRATOR_TARGET__DATABASE_URL="postgresql://postgres:PASSWORD@pgm-xxx.pg.rds.aliyuncs.com:5432/supabase_db" \ -e MIGRATOR_TARGET__SERVICE_ROLE_KEY="eyJ..." \ -e MIGRATOR_TARGET__API_BASE="http://your-rds-supabase/" \ supabase-migratorCLI migration (one-time task)
Run a CLI migration using Docker. Suitable for CI/CD pipelines or script automation. No Web UI is started:
docker run --rm \ -v ./config/default.yaml:/app/config/default.yaml:ro \ supabase-migrator rds-supabase-migrator migrate all -c /app/config/default.yaml
Method 3: Deploy from local source code
This method is for debugging or customizing the tool's logic. You must manually configure the Python and PostgreSQL client environments.
-
Install the PostgreSQL client tools (version 17 or later). For example, on macOS:
# Install PostgreSQL 17 client brew install postgresql@17 # Verify the version /opt/homebrew/opt/postgresql@17/bin/pg_dump --versionNoteYou do not need to add the client tools directory to your system's PATH. You can specify the directory path later using the
pg_bin_pathparameter in the configuration file. -
Create a Python virtual environment in the tool's directory and install the dependencies.
cd supabase-migrator python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt -
Install the migration tool in editable mode to register the
rds-supabase-migratorcommand.pip install -e .After installation, verify by running
--help:rds-supabase-migrator --help -
Create a configuration file from the example.
cp config/default.yaml.example config/default.yamlEdit
config/default.yamlas described in Step 1: Download the source code and configure migration parameters to specify the source and target connection details. -
Start the web UI service. Choose one of the following methods:
-
Recommended: Use the
servesubcommand (requires the previouspip install -e .step).source .venv/bin/activate rds-supabase-migrator serve --host 127.0.0.1 --port 8080 -
Alternative without package installation: Start directly via
uvicorn.source .venv/bin/activate PYTHONPATH=src python -m uvicorn supabase_migrator.main:app --host 127.0.0.1 --port 8080
After the service starts, open your browser and go to
http://127.0.0.1:8080to access the web UI. -
Step 3: Run the migration
Choose a mode based on your deployment method:
CLI mode
-
Validate connectivity. Run the validation command in the deployment directory to check whether the tool can access the source and target:
rds-supabase-migrator validate -c config/default.yamlNoteIf validation fails, check the following: ensure the egress IP of the migration tool has been added to all required IP whitelists, verify that the database name in the target connection string is
supabase_db, and confirm that the service_role key is copied completely. -
Run the migration. Choose a migration method based on your needs:
-
Full migration: Executes all enabled pipelines in the order of Auth, Database, Storage, and Edge Functions.
rds-supabase-migrator migrate all -c config/default.yaml -
Single-pipeline migration: Executes only the specified pipeline. Useful for retrying, partial migration, or staged migration.
# Migrate database only rds-supabase-migrator migrate database -c config/default.yaml # Migrate Storage only rds-supabase-migrator migrate storage -c config/default.yaml -
Dry run: Checks configuration and connectivity without performing any migration operations.
rds-supabase-migrator migrate all -c config/default.yaml --dry-run
ImportantIn CLI mode, migration logs are printed directly to the terminal. To run in the background, use
nohuportee. If interrupted, you can re-run the command. The tool automatically cleans up temporary files after each execution. -
Web UI mode
-
Open your browser and navigate to the migration tool's web UI. The address is
http://127.0.0.1:8080for a local deployment orhttp://<YOUR_MACHINE_IP>:8080for a Docker deployment. -
On the Configuration page, confirm that the source and target parameters from Step 1 are entered correctly. Click Test Connection to verify that the tool can connect to both the source and target.
NoteIf the test fails, check the following: ensure the egress IP of the migration tool has been added to all required IP whitelists, verify that the database name in the target connection string is
supabase_db, and confirm that the service_role key is copied completely. -
Navigate to the Migration page and choose a migration method:
-
Start Full Migration: Executes all enabled pipelines in the order of Auth, Database, Storage, and Edge Functions. Suitable for a complete project migration.
-
Migrate [Pipeline Name] Only: Executes a single pipeline. Useful for retrying, partial migration, or staged migration.
-
-
Navigate to the Logs page to monitor the migration progress, view the status of the current pipeline, and check for any errors.
ImportantDo not close the browser window or stop the migration tool process during the migration, as this will interrupt it. If an interruption occurs, you can restart the process. The tool uses a temporary directory for intermediate files and cleans it up after each run, so subsequent runs are not affected.
Verify the migration
After the migration is complete, verify the data integrity on the target RDS Supabase instance by checking the following areas:
-
Verify Auth data: Log in to the RDS Supabase Dashboard and go to Authentication > Users. Compare the total number of users to that of the source project.
-
Verify business data: Connect to the
supabase_dbdatabase on the target. Run the following SQL queries on both the source and target to compare the number of business tables and the row counts of key tables.-- Total number of tables SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'; -- Row count of a key table (replace with your actual table name) SELECT count(*) FROM public.your_table; -
Verify Storage files: In the RDS Supabase Dashboard, go to Storage. Confirm that the list of buckets is complete. Randomly check a few files in different buckets to ensure they can be previewed or downloaded correctly.
-
Verify Edge Functions: Go to the Edge Functions list and confirm that your functions have been deployed successfully. Use
curlor the test button in the Dashboard to invoke the functions and verify that they return the expected results.
CLI command reference
The CLI command structure is as follows:
rds-supabase-migrator <command> [pipeline] [options]
Supported commands:
-
validate: Validates configuration and connectivity without performing a migration. -
migrate: Runs the migration. Requires a pipeline to be specified. -
serve: Starts the Web UI service. Example:rds-supabase-migrator serve --host 127.0.0.1 --port 8080.
Pipelines supported by the migrate command:
-
all: Full migration (executes Auth, Database, Storage, and Edge Functions in order). -
auth: Migrates only user authentication data. -
database: Migrates only the business database. -
storage: Migrates only Storage files. -
edge-functions: Deploys only Edge Functions.
General parameters:
|
Parameter |
Required |
Description |
|
|
Yes |
Path to the configuration file, for example, |
|
|
No |
Overrides |
|
|
No |
Overrides |
|
|
No |
Overrides |
|
|
No |
Overrides |
|
|
No |
Dry run mode. Checks configuration and connectivity without performing any migration. |
|
|
No |
Displays debug-level logs for troubleshooting connection and authentication issues. |
|
|
No |
Only valid for |
|
|
No |
Only valid for |
Exit codes:
|
Exit code |
Meaning |
|
|
Migration succeeded. |
|
|
Configuration error or connection failure. |
|
|
One or more pipelines failed during migration. |
|
|
Migration interrupted by the user (received SIGINT, for example, Ctrl+C). |
API reference
When the Web UI is running (default address: http://127.0.0.1:8080), the tool also exposes the following HTTP endpoints for integration or automation scripts:
|
Method |
Path |
Description |
|
GET |
|
Retrieves the current configuration (sensitive fields are masked). |
|
PUT |
|
Updates the configuration. |
|
POST |
|
Tests source and target connectivity. |
|
POST |
|
Full migration. Executes all enabled pipelines in order. |
|
POST |
|
Migrates only Auth user data. |
|
POST |
|
Migrates only the database. |
|
POST |
|
Migrates only Storage files. |
|
POST |
|
Deploys only Edge Functions. |
|
POST |
|
Cancels the running migration task. |
|
GET |
|
Queries migration status and progress. |
|
GET |
|
Streams real-time logs via SSE (Server-Sent Events). |
FAQ
-
Q: Can I retry a migration after it fails?
A: Yes. Each migration run uses a separate temporary directory that is automatically cleaned up afterward, so it does not affect subsequent runs. You can simply click Start Full Migration again in the web UI. -
Q: How does the tool handle files in Storage that already exist on the target?
A: File uploads use upsert mode. Re-running the migration overwrites files with the same name on the target. The tool only creates buckets that do not exist on the target and does not modify existing bucket properties. -
Q: What should I do if the Database migration fails with a
pg_dump version mismatcherror?
A: Thepg_dumpclient version must be greater than or equal to the higher of the source and target server versions. In your configuration, point thepg_bin_pathto a directory that contains a more recent version of the PostgreSQL client tools. -
Q: What should I do if an Edge Functions deployment fails?
A: When using the API deployment method, ensure that theedge-runtime(agent-runtime) service is running correctly on the target and that thetarget.api_baseconfiguration parameter is set correctly. -
Q: Can I migrate only a single pipeline?
A: Yes. On the Migration page of the web UI, click the corresponding Migrate [Pipeline Name] Only button. Alternatively, call individual API endpoints such as/api/migrate/author/api/migrate/database. -
Q: How can I view logs in the Docker container?
A: You can view logs in two ways:-
View real-time logs on the Logs page of the web UI.
-
Run
docker logs -f migratoron the host machine to view the container's standard output.
-
-
Q: How can I view migration logs in CLI mode?
A: In CLI mode, logs are printed directly to the terminal. To save logs to a file, use output redirection:rds-supabase-migrator migrate all -c config/default.yaml 2>&1 | tee migrate.log. -
Q: What is the difference between RDS-to-RDS migration and Cloud-to-RDS migration?
A: The main differences are: (1) For RDS-to-RDS,management_api_tokenandproject_refon the source are left empty. The tool auto-detects RDS mode. (2) The source database must have a public endpoint enabled with the migration tool's IP added to the whitelist. (3) We recommend enablingclean_target_before_restoreto avoid conflicts with residual data.