> ## Documentation Index
> Fetch the complete documentation index at: https://planetscale.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Database imports

> Import an existing MySQL database into PlanetScale with no downtime by connecting it as an external keyspace and moving its tables with a Vitess MoveTables workflow.

## Overview

You can import an existing internet-accessible MySQL database into a PlanetScale Vitess database with **no downtime**. An import has two parts:

1. An [external keyspace](https://planetscale.com/docs/vitess/cluster-configuration.md#create-an-external-keyspace) connects your production branch to your existing MySQL database.
2. A [Vitess MoveTables](https://planetscale.com/docs/cli/move-tables.md) workflow copies the tables from the external keyspace into a PlanetScale keyspace, keeps them in sync while your application keeps running, and switches traffic to PlanetScale when you're ready.

You run the import with the [`pscale` CLI](https://planetscale.com/docs/cli). The **Workflows** page in the dashboard shows the progress of the import, but it is read-only: every step on this page is a CLI command.

> [!NOTE]
>
> You must be an [Organization Administrator](https://planetscale.com/docs/security/access-control.md#organization-administrator) or a [Database Administrator](https://planetscale.com/docs/security/access-control.md#database-administrator) of the database to run an import. To run an import with a [service token](https://planetscale.com/docs/api/service-tokens.md), give it the `create_branch`, `read_workflow`, `write_workflow`, and `delete_workflow` permissions on the database, plus `delete_production_branch` to delete the external keyspace at the end.

Before you begin, it may be helpful to check out our [general MySQL compatibility guide](https://planetscale.com/docs/vitess/troubleshooting/mysql-compatibility.md).

## Import process overview

1. **Prepare your source database** - Check its server settings, create a user for PlanetScale, and allow PlanetScale's IP addresses
2. **Create your PlanetScale database** - Create the Vitess database you are importing into, and tune its keyspace for the import
3. **Create an external keyspace** - Connect the production branch to your source database. PlanetScale checks connectivity, server settings, user grants, and schema compatibility first
4. **Start the import** - Create a MoveTables workflow from the external keyspace to your PlanetScale keyspace
5. **Monitor the import** - Watch the copy and replication progress, then verify the data
6. **Connect your application to PlanetScale** - Test your application against PlanetScale while your source database is still serving traffic
7. **Switch traffic** - Move reads, then writes, to PlanetScale
8. **Complete the import** - Stop replication and disconnect your source database

> [!NOTE]
>
> It's recommended to avoid all schema changes / DDL (Data Definition Language) statements during an import on both your source database and the PlanetScale database. This includes `CREATE`, `DROP`, `ALTER`, `TRUNCATE`, etc.

## Step 1: Prepare your source database

### Server configuration

These server settings need to be set correctly for the import to work:

| Variable | Required value | Documentation |
| :- | :- | :- |
| `gtid_mode` | `ON` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-gtids.html#sysvar_gtid_mode) |
| `log_bin` | `ON` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_log_bin) |
| `binlog_format` | `ROW` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_format) |
| `binlog_row_image` | `FULL` or `NOBLOB` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_row_image) |
| `expire_logs_days`\* | `>= 2` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_expire_logs_days) |
| `binlog_expire_logs_seconds`\* | `>= 172800` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_binlog_expire_logs_seconds) |
| `sql_mode` | Includes `NO_ZERO_IN_DATE` and `NO_ZERO_DATE`, and does not include `ANSI_QUOTES` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html) |
| `max_connections` | `>= 10` | [Documentation](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_connections) |

**\*** Either `expire_logs_days` or `binlog_expire_logs_seconds` needs to be set. If both are set, `binlog_expire_logs_seconds` takes precedence. On Amazon RDS and Aurora, set `binlog retention hours` to at least 48 instead. See the [provider-specific migration guides](#provider-specific-migration-guides) for how to change these settings.

MySQL 5.6, 5.7, and 8.0 are supported. The source database must not already contain a `_vt` database.

### Create a user for PlanetScale

PlanetScale connects to your source database with a single user. That user needs replication privileges, read and write access to the database you're importing, and access to the `ps_import_*` database that PlanetScale creates on your source to track replication. See [Import user permissions](https://planetscale.com/docs/vitess/imports/import-tool-user-requirements.md) for the full list of grants and a script that creates the user.

### Allow PlanetScale's IP addresses

Allow connections from PlanetScale's IP addresses for your database's region in your database firewall or security group. See [Import public IP addresses](https://planetscale.com/docs/vitess/imports/import-tool-migration-addresses.md) for where to find them.

## Step 2: Create your PlanetScale database

Create the Vitess database you're importing into:

```bash theme={null}
pscale database create <DATABASE_NAME> --org <ORGANIZATION_NAME> --region <REGION> --cluster-size <CLUSTER_SIZE> --wait
```

Pick a [region](https://planetscale.com/docs/plans/regions.md) close to your source database and a [cluster size](https://planetscale.com/docs/vitess/scaling/cluster-sizing.md) with enough storage for your data. You can also create the database from the dashboard with "**New database**" > "**Create database**".

We recommend using the same name as the database you're importing from to avoid updating any database name references throughout your application code. Your PlanetScale database starts with one keyspace, named after the database. That keyspace is the **target** of the import. If you'd prefer to use a different database name, make sure to update your app where applicable once you fully switch over to PlanetScale.

### Tune the target keyspace for the import

We recommend setting these [VTTablet parameters](https://planetscale.com/docs/vitess/cluster-configuration/parameters.md) on your PlanetScale keyspace before you start the import. They speed up the copy and keep VReplication retrying through errors on your source database, which matters most for large imports.

| Parameter | Value for imports | What it does |
| :- | :- | :- |
| `vreplication_copy_phase_duration` | `24h` | How long each copy phase runs before the next catch-up phase. Only change it if your database uses [foreign key constraints](#foreign-key-constraints). |
| `vreplication_max_time_to_retry_on_error` | `720h` | How long VReplication keeps retrying after an error before it gives up. |
| `vreplication-parallel-insert-workers` | `4` | Number of parallel insert workers on the target tablet during the copy. Don't set it higher than the number of vCPUs on the keyspace's tablets. |

Set them with [`pscale keyspace parameters set`](https://planetscale.com/docs/cli/keyspace#change-keyspace-parameters). If your database uses foreign key constraints, also pass `--parameters vttablet.vreplication_copy_phase_duration=24h`:

```bash theme={null}
pscale keyspace parameters set <DATABASE_NAME> main <TARGET_KEYSPACE_NAME> \
  --org <ORGANIZATION_NAME> \
  --parameters vttablet.vreplication-parallel-insert-workers=4 \
  --parameters vttablet.vreplication_max_time_to_retry_on_error=720h
```

The change rolls out to the keyspace's tablets, one shard at a time by default. Wait until `pscale keyspace parameters changes list <DATABASE_NAME> main <TARGET_KEYSPACE_NAME> --org <ORGANIZATION_NAME>` shows it as `completed` before you start the import. You can also set these parameters in the dashboard: click **Clusters** in the left nav, select the keyspace, click the **VTTablets** tab, and search for each parameter.

Set these parameters back to their defaults after you [switch primary traffic](#step-7-switch-traffic) to PlanetScale.

## Step 3: Create an external keyspace

An external keyspace connects your production branch to your source database. It is the **source** of the import. Give it a name that is different from your PlanetScale keyspace, such as `mydb_source`.

### Connection settings

| Setting | `pscale keyspace create-external` flag | Description |
| :- | :- | :- |
| Host name | `--host` | The address where your database is hosted. |
| Port | `--port` | The port where your database is hosted. The default MySQL port is `3306`. |
| Database name | `--source-database` | The exact name of the database you want to import on your MySQL server. |
| Username | `--username` | The [user you created for PlanetScale](https://planetscale.com/docs/vitess/imports/import-tool-user-requirements.md). |
| Password | `--password` | The password for that user. |
| SSL verification mode | `--ssl-mode` | `disabled`, `preferred`, `required`, `verify_ca`, or `verify_identity`. |
| SSL client certificate and key | `--ssl-client-certificate`, `--ssl-client-key` | Authenticate with mTLS instead of a password. Pass the PEM contents or a path to a PEM file. |
| SSL CA certificate chain | `--ssl-certificate-authority` | If your database server has a certificate with a non-trusted root CA, provide the full CA certificate chain here. |
| SSL server name override | `--ssl-server-name` | Override the server name for SSL certificate verification. |
| Minimum TLS version | `--min-tls-version` | The oldest TLS version PlanetScale may use. The default is TLS 1.2. |

If your database server has a valid SSL certificate, set the SSL verification mode to `required` or higher. For more information about certificates from a Certificate Authority, check out our [Secure connections documentation](https://planetscale.com/docs/vitess/connecting/secure-connections.md#certificate-authorities).

### Check your source database

Run `create-external` with `--dry-run` to check your source database without creating anything:

```bash theme={null}
pscale keyspace create-external <DATABASE_NAME> main <EXTERNAL_KEYSPACE_NAME> \
  --org <ORGANIZATION_NAME> \
  --host <SOURCE_HOST> \
  --source-database <SOURCE_DATABASE_NAME> \
  --username <SOURCE_USERNAME> \
  --password <PASSWORD> \
  --ssl-mode required \
  --dry-run
```

PlanetScale checks:

* **Connectivity** - It can connect with the credentials and SSL/TLS settings you provided.
* **Server configuration** - The [server settings](#server-configuration) above are correct.
* **User grants** - The user has the [required grants](https://planetscale.com/docs/vitess/imports/import-tool-user-requirements.md).
* **Schema compatibility** - Every table can be imported. See [Schema compatibility](#schema-compatibility) below.

If PlanetScale cannot connect, or the server settings or grants are wrong, it won't create the external keyspace. Fix the issue on your source database and run the check again. The [Import troubleshooting guide](https://planetscale.com/docs/vitess/imports/import-troubleshooting.md) covers each error.

### Schema compatibility

The check also reports tables that can't be imported:

* **Missing unique key** - All tables must have a unique, not-null key. See our [Changing unique keys documentation](https://planetscale.com/docs/vitess/schema-changes/onlineddl-change-unique-keys.md) for more info.
* **Invalid charset** - We support `utf8`, `utf8mb4`, `utf8mb3`, `latin1`, and `ascii`. Tables with other charsets will be flagged.
* **Unsupported storage engines** - Only `InnoDB` is supported.
* **Unsupported partitioning** - Only `RANGE` partitioning is supported, without subpartitions.

Schema errors don't stop you from creating the external keyspace. Fix those tables on your source database, or leave them out of the import with `--exclude-tables` in the next step.

If your database uses foreign key constraints, PlanetScale turns on [foreign key support](https://planetscale.com/docs/vitess/foreign-key-constraints.md) for your PlanetScale database when you create the external keyspace. See [Foreign key constraints](#foreign-key-constraints) before you start the import.

### Create the external keyspace

Once the check passes, run the same command with `--wait` instead of `--dry-run`:

```bash theme={null}
pscale keyspace create-external <DATABASE_NAME> main <EXTERNAL_KEYSPACE_NAME> \
  --org <ORGANIZATION_NAME> \
  --host <SOURCE_HOST> \
  --source-database <SOURCE_DATABASE_NAME> \
  --username <SOURCE_USERNAME> \
  --password <PASSWORD> \
  --ssl-mode required \
  --wait
```

PlanetScale sizes the external keyspace from the size of your source data.

You can also create the external keyspace from the dashboard: open your production branch, click "**Clusters**", then "**New keyspace**" > "**Create external keyspace**". The form checks your source database the same way before it creates the keyspace, and lists the IP addresses to allow.

## Step 4: Start the import

Create a MoveTables workflow that copies the tables from the external keyspace into your PlanetScale keyspace:

```bash theme={null}
pscale branch vtctl move-tables create <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --source-keyspace <EXTERNAL_KEYSPACE_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --all-tables \
  --defer-secondary-keys \
  --format json
```

The workflow starts right away. You will pass the workflow name and the target keyspace to every later command.

### Import options

* **Tables** - `--all-tables` imports every table. To import only some tables, pass `--tables` with a comma-separated list instead. To import every table except a few, combine `--all-tables` with `--exclude-tables`.
* **Defer secondary index creation** - `--defer-secondary-keys` creates secondary (non-primary) indexes after the data is copied instead of during the copy. Maintaining many indexes while inserting data is slow, so this can make your import significantly faster (often 2-3x faster for tables with multiple indexes). Secondary indexes are created during the copy unless you pass this flag. Don't use it if your database has foreign key constraints.
* **DDL handling** - `--on-ddl` controls what happens if schema changes (like `ALTER TABLE`, `ADD INDEX`, etc.) run on your source database while the import is running:
  * `STOP` (default, recommended) - The workflow stops when a schema change is detected. After you review the change, restart the workflow with `pscale branch vtctl move-tables start`. This is the safest option because it lets you verify the schema changes won't cause issues before continuing.
  * `IGNORE` - Schema changes are skipped and won't be applied to your PlanetScale database. Your import continues without interruption, but your schemas will diverge. Only use this if you're confident you don't need these changes or plan to apply them manually to your PlanetScale database later.
  * `EXEC` - Schema changes are applied to your PlanetScale database while the import continues running. If applying a schema change fails (for example, if it's not compatible with Vitess), the workflow stops and you'll need to restart it.
  * `EXEC_IGNORE` - Attempts to apply schema changes but keeps running even if they fail.

> [!WARNING]
>
> `EXEC_IGNORE` can lead to schema mismatches between your external database and PlanetScale database, potentially causing data inconsistencies or unexpected behavior. Only use this if you understand the risks and have a plan to handle failures.

See the [`move-tables` reference](https://planetscale.com/docs/cli/move-tables) for every option.

### Foreign key constraints

If your database uses foreign key constraints:

* **Import all tables** - Pass `--all-tables` without `--exclude-tables` so referential integrity stays intact.
* **Use an atomic copy** - Pass `--atomic-copy`, and don't pass `--defer-secondary-keys`. Foreign key constraints need their indexes to exist during the copy.
* **Import retries** - An atomic copy holds a long-running transaction on your source database, which can increase load. If it fails, it starts over from the beginning instead of resuming where it left off.

```bash theme={null}
pscale branch vtctl move-tables create <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --source-keyspace <EXTERNAL_KEYSPACE_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --all-tables \
  --atomic-copy \
  --format json
```

For more information about foreign key support and limitations, see our [foreign key constraints documentation](https://planetscale.com/docs/vitess/foreign-key-constraints.md).

## Step 5: Monitor your import

Check the progress of the import with `status`:

```bash theme={null}
pscale branch vtctl move-tables status <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --format json
```

The output shows:

* **`table_copy_state`** - Rows and bytes copied so far for each table that is still copying
* **`shard_streams`** - Each replication stream's state and any error message
* **`traffic_state`** - Whether reads and writes have switched to PlanetScale

Streams are `Copying` while the initial data is copied, then `Running` once the import is replicating new changes from your source database. A `Lagging` stream is running but hasn't caught up with recent changes. A `Stopped` or `Error` stream includes a message that explains why. The output also includes a `next_steps` field with the command to run next.

You can also follow the import in the dashboard. Click "**Workflows**" in the left nav, select your production branch, and open the workflow to see its streams, per-table copy progress, replication lag, and traffic routing.

To pause the import, run `pscale branch vtctl move-tables stop`. Run `pscale branch vtctl move-tables start` to resume it.

### Verify data (optional)

Once the copy completes and the streams are `Running`, you can verify that the data in PlanetScale matches your source database with a [VDiff](https://vitess.io/docs/reference/vreplication/vdiff/):

```bash theme={null}
pscale branch vtctl vdiff create <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --format json
```

The output includes the VDiff's `uuid`. Pass it to `vdiff show` and repeat until the VDiff completes:

```bash theme={null}
pscale branch vtctl vdiff show <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --uuid <VDIFF_UUID> \
  --format json
```

The dashboard also shows the VDiff's progress on the workflow page. If the VDiff reports mismatches, don't switch traffic until you've resolved them.

## Step 6: Connect your application to PlanetScale

Once the streams are `Running`, you can connect your application to PlanetScale while your source database stays the authoritative source. Until you switch traffic, PlanetScale routes queries for the imported tables to your source database through the external keyspace, so reads and writes still happen there.

[Create a password](https://planetscale.com/docs/vitess/connecting/connection-strings.md) for your production branch and point a test deployment of your application at PlanetScale. This is the ideal time to test your application end-to-end before switching traffic.

## Step 7: Switch traffic

When you're ready, switch traffic to PlanetScale. You can switch replica traffic first to test reads, then switch primary traffic.

> [!WARNING]
>
> **Before switching primary traffic:** Update connection strings for all production writers to PlanetScale, and verify none still writes to the source database. Otherwise, the databases can diverge and data can be lost.

1. **Switch replica traffic** - Serve read queries sent to replicas from PlanetScale while writes still go to your source database. This is an optional intermediate step that lets you test read traffic separately.

   ```bash theme={null}
   pscale branch vtctl move-tables switch-traffic <DATABASE_NAME> main \
     --org <ORGANIZATION_NAME> \
     --workflow <WORKFLOW_NAME> \
     --target-keyspace <TARGET_KEYSPACE_NAME> \
     --tablet-types REPLICA,RDONLY \
     --format json
   ```

2. **Switch primary traffic** - Serve both reads and writes from PlanetScale.

   ```bash theme={null}
   pscale branch vtctl move-tables switch-traffic <DATABASE_NAME> main \
     --org <ORGANIZATION_NAME> \
     --workflow <WORKFLOW_NAME> \
     --target-keyspace <TARGET_KEYSPACE_NAME> \
     --tablet-types PRIMARY \
     --format json
   ```

Pass `--dry-run` to see what a switch would do without applying it.

After primary traffic switches, PlanetScale replicates writes back to your source database, so both stay in sync until you complete the import. If something goes wrong, switch traffic back to your source database:

```bash theme={null}
pscale branch vtctl move-tables reverse-traffic <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --format json
```

If you [tuned the target keyspace](#tune-the-target-keyspace-for-the-import) for the import, set its VTTablet parameters back to their defaults once primary traffic has switched. Like the original change, this rolls out to the keyspace's tablets one shard at a time.

```bash theme={null}
pscale keyspace parameters set <DATABASE_NAME> main <TARGET_KEYSPACE_NAME> \
  --org <ORGANIZATION_NAME> \
  --reset vttablet.vreplication-parallel-insert-workers \
  --reset vttablet.vreplication_max_time_to_retry_on_error
```

Add `--reset vttablet.vreplication_copy_phase_duration` if you set it for foreign key constraints.

## Step 8: Complete the import

Once you've switched all traffic to PlanetScale and verified everything is working:

**Step 1**

Monitor your application for any issues. Check:

* Application logs for errors
* Replication lag (should be near zero)
* Error tracking tools
* Application performance metrics

**Step 2**

Preview what completing the workflow will do:

```bash theme={null}
pscale branch vtctl move-tables complete <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --keep-data=true \
  --keep-routing-rules=false \
  --dry-run \
  --format json
```

**Step 3**

When you're confident everything is working correctly, run the same command without `--dry-run`.

**Step 4**

Delete the external keyspace to disconnect PlanetScale from your source database:

```bash theme={null}
pscale keyspace delete <DATABASE_NAME> main <EXTERNAL_KEYSPACE_NAME> --org <ORGANIZATION_NAME>
```

`--keep-data` must be `true` when you complete an import. Your source database's tables are never dropped or renamed. `--keep-routing-rules=false` removes the routing rules that sent queries to the external keyspace. Write both flags with an `=`: a space-separated value such as `--keep-data false` is read as `--keep-data=true`.

**What happens when you complete:**

* Replication from PlanetScale back to your source database stops
* The routing rules for the imported tables are removed
* The workflow no longer appears on the Workflows page

**What happens when you delete the external keyspace:**

* PlanetScale disconnects from your source database
* The source database's credentials are removed from PlanetScale

> [!WARNING]
>
> **Important**
>
> Completing the workflow is not reversible. Make sure your application is running smoothly on PlanetScale before completing the import. Don't delete the external keyspace until the workflow is complete.

Once the import is complete, you can drop the `ps_import_*` database from your source database and remove the user you created for PlanetScale.

### Cancel an import

To stop an import before you complete it, cancel the workflow:

```bash theme={null}
pscale branch vtctl move-tables cancel <DATABASE_NAME> main \
  --org <ORGANIZATION_NAME> \
  --workflow <WORKFLOW_NAME> \
  --target-keyspace <TARGET_KEYSPACE_NAME> \
  --keep-data=false \
  --keep-routing-rules=false \
  --format json
```

`--keep-data=false` deletes the data already copied into your PlanetScale keyspace. The external keyspace stays connected, so you can fix the problem and start a new workflow. Delete the external keyspace if you don't plan to try again.

## Next steps

You just migrated your database to PlanetScale. Here are some things you can do next:

* [Create a development branch](https://planetscale.com/docs/vitess/schema-changes/branching.md) - Use branching in your development workflow.
* [Create a deploy request](https://planetscale.com/docs/vitess/schema-changes/deploy-requests.md#create-a-deploy-request) - Test schema changes in dev branches before pushing to production.

## Provider-specific migration guides

For detailed instructions on preparing your external database for import, see our provider-specific guides:

* [Amazon Aurora](https://planetscale.com/docs/vitess/imports/amazon-aurora-migration-guide.md)
* [AWS RDS for MySQL](https://planetscale.com/docs/vitess/imports/aws-rds-migration-guide.md)
* [Azure Database for MySQL](https://planetscale.com/docs/vitess/imports/azure-database-for-mysql-migration-guide.md)
* [DigitalOcean MySQL](https://planetscale.com/docs/vitess/imports/digitalocean-database-migration-guide.md)
* [Google Cloud SQL](https://planetscale.com/docs/vitess/imports/gcp-cloudsql-migration-guide.md)
* [MariaDB](https://planetscale.com/docs/vitess/imports/mariadb-migration-guide.md)

## Troubleshooting

For detailed troubleshooting guidance, see our [Import troubleshooting guide](https://planetscale.com/docs/vitess/imports/import-troubleshooting.md).

## Need help?

Get help from [the PlanetScale Support team](https://planetscale.com/contact?initial=support), or join our [Discord community](https://pscale.link/community) to see how others are using PlanetScale.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.