-
Notifications
You must be signed in to change notification settings - Fork 990
docs(docs): refresh PostgreSQL migration guides #8199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
306 changes: 275 additions & 31 deletions
306
...ntent/docs/(index)/prisma-postgres/import-from-existing-database-postgresql.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,60 +1,304 @@ | ||
| --- | ||
| title: Import from PostgreSQL | ||
| description: 'Import an existing PostgreSQL database into Prisma Postgres, then use it with Prisma 8.' | ||
| description: 'Move an existing PostgreSQL database to Prisma Postgres without changing your ORM version.' | ||
| url: /prisma-postgres/import-from-existing-database-postgresql | ||
| metaTitle: Import from PostgreSQL into Prisma Postgres for Prisma 8 | ||
| metaDescription: 'Import PostgreSQL data into Prisma Postgres, then connect Prisma 8 to the imported database.' | ||
| metaTitle: Import an existing PostgreSQL database into Prisma Postgres | ||
| metaDescription: 'Export an existing PostgreSQL database, restore it into Prisma Postgres, and reconnect your application.' | ||
| --- | ||
|
|
||
| Move an existing PostgreSQL database into Prisma Postgres, then connect Prisma 8 to it. | ||
| Move an existing PostgreSQL database to Prisma Postgres with `pg_dump` and `pg_restore`, then reconnect and verify your application. | ||
|
|
||
| This guide works with Prisma 8, Prisma 7, earlier Prisma ORM versions, and standard PostgreSQL clients. Moving your database does not require changing your ORM version. | ||
|
|
||
| :::warning[This migration copies a snapshot] | ||
|
|
||
| The final dump contains the database state from when `pg_dump` runs. For a production cutover, stop or drain source writes before the final dump and keep them stopped until validation succeeds and traffic switches to Prisma Postgres. | ||
|
|
||
| ::: | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| You need: | ||
|
|
||
| - the connection URL for the PostgreSQL database you are importing from | ||
| - a Prisma Data Platform account | ||
| - PostgreSQL 17 CLI tools, including `pg_dump` and `pg_restore` | ||
| - Node.js 24 or newer | ||
| - a direct, non-pooled connection URL for the PostgreSQL database you are moving | ||
| - a new, empty [Prisma Postgres database](https://console.prisma.io) | ||
| - the [direct and pooled Prisma Postgres connection strings](/postgres/database/connecting-to-your-database) | ||
| - enough local disk space for the compressed database dump | ||
| - PostgreSQL 17 command-line tools: `pg_dump`, `pg_restore`, and `psql` | ||
|
|
||
| Confirm that all three tools use PostgreSQL 17: | ||
|
|
||
| ```bash title="Terminal" | ||
| pg_dump --version | ||
| pg_restore --version | ||
| psql --version | ||
| ``` | ||
|
|
||
| Each command should report version `17.x`. PostgreSQL 17 `pg_dump` can read older PostgreSQL databases, but it cannot dump a server newer than version 17. Moving from PostgreSQL 18 or later into Prisma Postgres requires a separate downgrade-compatible migration process. | ||
|
|
||
| ## 1. Inspect the source database | ||
|
|
||
| Set the direct source connection URL. Keep the single quotes so your shell does not interpret special characters in the URL: | ||
|
|
||
| ```bash title="Terminal" | ||
| export SOURCE_DATABASE_URL='postgresql://USER:PASSWORD@HOST:5432/DATABASE?sslmode=require' | ||
| ``` | ||
|
|
||
| Check the source version and database size before you create the dump: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$SOURCE_DATABASE_URL" \ | ||
| -X \ | ||
| --set ON_ERROR_STOP=1 \ | ||
| --command="SELECT current_setting('server_version') AS server_version, pg_size_pretty(pg_database_size(current_database())) AS database_size;" | ||
| ``` | ||
|
|
||
| The command should print the server version and database size. Stop if the server is newer than PostgreSQL 17 or if you do not have enough local disk space for the dump. | ||
|
|
||
| List the application schemas and installed extensions: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$SOURCE_DATABASE_URL" \ | ||
| -X \ | ||
| --set ON_ERROR_STOP=1 \ | ||
| --command="SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema') AND schema_name NOT LIKE 'pg_%' ORDER BY schema_name;" \ | ||
| --command="SELECT extname FROM pg_extension ORDER BY extname;" | ||
| ``` | ||
|
|
||
| Compare the result with the [extensions supported by Prisma Postgres](/postgres/database/postgres-extensions). Stop and decide how to replace or remove an unsupported extension before continuing. | ||
|
|
||
| `pg_dump` does not copy PostgreSQL roles. Inspect policies that name roles so you can adapt them for the target database: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$SOURCE_DATABASE_URL" \ | ||
| -X \ | ||
| --set ON_ERROR_STOP=1 \ | ||
| --command="SELECT schemaname, tablename, policyname, roles FROM pg_policies ORDER BY schemaname, tablename, policyname;" | ||
| ``` | ||
|
|
||
| If a policy depends on a source-only role, record its policy name. You will omit that policy during restore and recreate it for your Prisma Postgres access model before switching the application. | ||
|
|
||
| ## 2. Create the Prisma Postgres destination | ||
|
|
||
| Create a new Prisma Postgres database in [Prisma Console](https://console.prisma.io): | ||
|
|
||
| 1. Open your project and select the database. | ||
| 1. Click **Connect to your database**. | ||
| 1. Click **Generate new connection string**. | ||
| 1. Copy both the **direct** and **pooled** connection strings. | ||
|
|
||
| Set the direct connection string for the import: | ||
|
|
||
| ```bash title="Terminal" | ||
| export PRISMA_POSTGRES_DIRECT_URL='postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require' | ||
| ``` | ||
|
|
||
| Confirm that the target is reachable and empty: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$PRISMA_POSTGRES_DIRECT_URL" \ | ||
| -X \ | ||
| --set ON_ERROR_STOP=1 \ | ||
| --command="SELECT current_database(), current_setting('server_version');" \ | ||
| --command="SELECT schemaname, tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY schemaname, tablename;" | ||
| ``` | ||
|
|
||
| The first query should report the Prisma Postgres database and PostgreSQL 17. The second query should not list any application tables. Create another Prisma Postgres database if this target already contains data you need to keep. | ||
|
|
||
| ## 3. Stop source writes and export the final snapshot | ||
|
|
||
| You can rehearse this process while the source remains active, but do not use a rehearsal dump for the production cutover. Before the final dump, stop or drain every application, worker, scheduled job, integration, and administrative operation that can write to the source. Confirm that writes have stopped, and keep the source frozen until step 7 switches traffic to Prisma Postgres. | ||
|
|
||
| If you cannot keep the source frozen for the dump, restore, and validation window, stop here. Use a separately tested migration process that durably captures and replays every post-snapshot write. This snapshot-only process does not provide a zero-downtime cutover. | ||
|
|
||
| Create a private, compressed archive containing all non-system schemas in the selected database. See the [PostgreSQL 17 `pg_dump` reference](https://www.postgresql.org/docs/17/app-pgdump.html) for the other options: | ||
|
|
||
| ```bash title="Terminal" | ||
| umask 077 | ||
|
|
||
| pg_dump \ | ||
| --format=custom \ | ||
| --verbose \ | ||
| --no-owner \ | ||
| --no-privileges \ | ||
| --dbname="$SOURCE_DATABASE_URL" \ | ||
| --file=postgres-to-prisma-postgres.dump | ||
| ``` | ||
|
|
||
| `pg_dump` should finish without an error and create `postgres-to-prisma-postgres.dump`. Treat this file as sensitive because it contains your application data. | ||
|
|
||
| Create the restore manifest and confirm that PostgreSQL can read the archive: | ||
|
|
||
| ```bash title="Terminal" | ||
| pg_restore --list postgres-to-prisma-postgres.dump > postgres-restore.list | ||
| sed -n '1,20p' postgres-restore.list | ||
| ``` | ||
|
|
||
| You should see an archive header followed by database objects. If `pg_dump` reports a version mismatch, make sure the PostgreSQL 17 tools appear first in your `PATH`. | ||
|
|
||
| If the policy check found policies that reference source-only roles, list the policy entries: | ||
|
|
||
| ```bash title="Terminal" | ||
| grep ' POLICY ' postgres-restore.list | ||
| ``` | ||
|
|
||
| In a text editor, prefix with `;` only the `POLICY` lines for the policy names you recorded. This omits the incompatible policies without omitting their tables or data. Recreate each omitted policy with target-compatible roles after the restore and before switching the application. | ||
|
|
||
| ## 4. Restore into Prisma Postgres | ||
|
|
||
| Restore the archive through the direct Prisma Postgres connection: | ||
|
|
||
| ```bash title="Terminal" | ||
| pg_restore \ | ||
| --verbose \ | ||
| --single-transaction \ | ||
| --exit-on-error \ | ||
| --no-owner \ | ||
| --no-privileges \ | ||
| --use-list=postgres-restore.list \ | ||
| --dbname="$PRISMA_POSTGRES_DIRECT_URL" \ | ||
| postgres-to-prisma-postgres.dump | ||
| ``` | ||
|
|
||
| The command should finish with exit code `0`. The single transaction leaves the target unchanged if an error stops the restore. Do not ignore errors about missing extensions, roles, or incompatible database objects. | ||
|
|
||
| Refresh PostgreSQL's query-planner statistics after the restore: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$PRISMA_POSTGRES_DIRECT_URL" \ | ||
| -X \ | ||
| --set ON_ERROR_STOP=1 \ | ||
| --command="ANALYZE;" | ||
| ``` | ||
|
|
||
| ## 5. Verify the imported data | ||
|
|
||
| Create a reusable query that returns an exact row count for every non-system table: | ||
|
|
||
| ```sql title="row-counts.sql" | ||
| \pset tuples_only on | ||
| \pset format unaligned | ||
|
|
||
| SELECT format( | ||
| 'SELECT %L || ''='' || count(*) FROM %I.%I;', | ||
| schemaname || '.' || tablename, | ||
| schemaname, | ||
| tablename | ||
| ) | ||
| FROM pg_tables | ||
| WHERE schemaname NOT IN ('pg_catalog', 'information_schema') | ||
| ORDER BY schemaname, tablename | ||
| \gexec | ||
| ``` | ||
|
|
||
| With source writes still stopped, run the query against both databases and compare the results: | ||
|
|
||
| ```bash title="Terminal" | ||
| psql "$SOURCE_DATABASE_URL" -X --set ON_ERROR_STOP=1 --file=row-counts.sql > source-row-counts.txt | ||
| psql "$PRISMA_POSTGRES_DIRECT_URL" -X --set ON_ERROR_STOP=1 --file=row-counts.sql > target-row-counts.txt | ||
| diff -u source-row-counts.txt target-row-counts.txt | ||
| ``` | ||
|
|
||
| `diff` should print nothing and exit with code `0`. Also rerun the schema, extension, and policy queries from step 1 against `PRISMA_POSTGRES_DIRECT_URL`. Resolve every unexpected difference while source writes remain stopped. Do not send production traffic to Prisma Postgres yet. | ||
|
|
||
| ## 6. Connect your application | ||
|
|
||
| Choose the path that matches your application: | ||
|
|
||
| - [I already use Prisma 8](#already-use-prisma-8) | ||
| - [I use Prisma 7 or earlier](#use-prisma-7-or-earlier) | ||
| - [I do not use Prisma ORM](#do-not-use-prisma-orm) | ||
|
|
||
| Every path uses the two connection strings from step 2: the pooled URL for application queries and the direct URL for migrations and other CLI tools. | ||
|
|
||
| ### Already use Prisma 8 | ||
|
|
||
| ## 1. Create a Prisma Postgres database | ||
| Keep your existing contract, migrations, and application code. The dump copied the `prisma_contract` schema, so the imported database still carries the marker that ties it to your contract. | ||
|
|
||
| Create a Prisma Postgres database from Console or with the CLI. Copy the direct connection string. You will use it for the restore and for `DATABASE_URL`. | ||
| Set the pooled URL for runtime queries and the direct URL for Prisma CLI commands: | ||
|
|
||
| ## 2. Export from PostgreSQL | ||
| ```bash title=".env" | ||
| DATABASE_URL="postgres://USER:PASSWORD@pooled.db.prisma.io:5432/postgres?sslmode=require" | ||
| DIRECT_URL="postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require" | ||
| ``` | ||
|
|
||
| If your `prisma.config.ts` sets `db.connection`, point it at `DIRECT_URL`. | ||
|
|
||
| Confirm that the imported database still matches your contract: | ||
|
|
||
| ```npm title="Terminal" | ||
| npx prisma@latest db verify --db "$DIRECT_URL" | ||
| ``` | ||
|
|
||
| The command exits with code `0` when the marker and schema match the contract. Exit code `4` means the database does not match. Check that the restore included the `prisma_contract` schema before you change anything else. Do not run `orm init` or `contract infer` against the new database; your existing contract already describes it. | ||
|
|
||
| ### Use Prisma 7 or earlier | ||
|
|
||
| Set the pooled URL for runtime queries and the direct URL for Prisma CLI commands: | ||
|
|
||
| ```bash title=".env" | ||
| DATABASE_URL="postgres://USER:PASSWORD@pooled.db.prisma.io:5432/postgres?sslmode=require" | ||
| DIRECT_URL="postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require" | ||
| ``` | ||
|
|
||
| Prisma 7 reads the CLI connection from `datasource.url` in `prisma.config.ts`. Point it at `DIRECT_URL` as shown in the [Prisma 7 PostgreSQL setup](/orm/v7/core-concepts/supported-databases/postgresql). Prisma 6 and earlier read `url` and `directUrl` from the `datasource` block in `schema.prisma`. | ||
|
|
||
| If your application did not use Prisma Accelerate, run `npx prisma generate` and continue to step 7. | ||
|
|
||
| Run `pg_dump` against the source database: | ||
| #### Remove Prisma Accelerate | ||
|
|
||
| ```shell | ||
| pg_dump -Fc -v -d "postgresql://USER:PASSWORD@HOST:PORT/DATABASE" -n public -f db_dump.bak | ||
| If your application connected through Accelerate, remove it now. Prisma Postgres connection pooling replaces the Accelerate connection pool. It does not replace Accelerate query caching, so remove the cache calls as well. | ||
|
|
||
| Uninstall the extension: | ||
|
|
||
| ```npm title="Terminal" | ||
| npm uninstall @prisma/extension-accelerate | ||
| ``` | ||
|
|
||
| ## 3. Restore into Prisma Postgres | ||
| In your Prisma Client setup, remove the `@prisma/extension-accelerate` import, `withAccelerate()`, and `accelerateUrl`. Remove `cacheStrategy` options from queries, and remove calls to `withAccelerateInfo`, `$accelerate.invalidate`, and `$accelerate.invalidateAll`. | ||
|
|
||
| Undo any build settings you added for Accelerate: imports from `@prisma/client/edge`, `engineType = "client"` in the generator block, and the `--no-engine`, `--accelerate`, or `--data-proxy` flags on `prisma generate`. For Prisma 7, connect through the PostgreSQL driver adapter. For Prisma 6 or earlier, use Prisma Client with its bundled query engine or a driver adapter your version supports. | ||
|
|
||
| Restore the dump with the direct Prisma Postgres connection string: | ||
| Accelerate reached edge runtimes over HTTP. Prisma Postgres connections use PostgreSQL over TCP. If your application runs in a runtime that cannot open TCP connections, move database access to a Node.js or Bun runtime before switching. The [Prisma Postgres serverless driver](/postgres/database/serverless-driver) works in constrained runtimes but is in Early Access and not recommended for production. | ||
|
|
||
| ```shell | ||
| pg_restore -d "postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require" -v ./db_dump.bak | ||
| Regenerate Prisma Client: | ||
|
|
||
| ```npm title="Terminal" | ||
| npx prisma generate | ||
| ``` | ||
|
|
||
| ## 4. Add Prisma 8 | ||
| Search your project for `@prisma/extension-accelerate` and `prisma://`. Neither should appear. | ||
|
|
||
| ### Do not use Prisma ORM | ||
|
|
||
| From your app root, initialize Prisma 8: | ||
| Keep your existing PostgreSQL client library. Point its runtime connection at the pooled URL, and use the direct URL for migrations, `pg_dump`, `pg_restore`, and other administrative tools: | ||
|
|
||
| ```npm | ||
| npx prisma@latest orm init | ||
| ```bash title=".env" | ||
| DATABASE_URL="postgres://USER:PASSWORD@pooled.db.prisma.io:5432/postgres?sslmode=require" | ||
| DIRECT_URL="postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require" | ||
| ``` | ||
|
|
||
| Choose PostgreSQL, set `DATABASE_URL` to the Prisma Postgres connection string, then infer and emit the contract: | ||
| ## 7. Verify and switch the application | ||
|
|
||
| Start the application with the new environment variables while production traffic remains paused, then run its existing test suite. At minimum, verify one read and one rolled-back or disposable write through the pooled connection. | ||
|
|
||
| ```npm | ||
| npx prisma@latest contract infer --output ./prisma/contract.prisma | ||
| npx prisma@latest contract emit | ||
| npx prisma@latest db sign | ||
| Only after the database comparison and application checks succeed, route production traffic to Prisma Postgres and resume writes there. Do not re-enable writes on the source. Keep the source database available in a read-only state until the application is healthy on Prisma Postgres and the rollback window has ended. | ||
|
|
||
| ## 8. Delete the local dump files | ||
|
|
||
| After you have verified the application and retained any audit evidence you need, delete the local dump and row-count files: | ||
|
|
||
| ```bash title="Terminal" | ||
| rm postgres-to-prisma-postgres.dump postgres-restore.list row-counts.sql source-row-counts.txt target-row-counts.txt | ||
| ``` | ||
|
|
||
| ## Next steps | ||
| ## Recommended next step: adopt Prisma 8 | ||
|
|
||
| Your database migration is complete. Prisma 8 is the current Prisma ORM release, but upgrading is a separate application change. Follow the [incremental Prisma 7 to Prisma 8 guide](/guides/upgrade-prisma-orm/postgresql) after the application is stable on Prisma Postgres. | ||
|
|
||
| If you want to complete both changes on the same day, deploy and verify the Prisma Postgres connection first. Upgrade Prisma ORM in a separate commit or deployment so failures have one clear cause. | ||
|
|
||
| ## Provider-specific guides | ||
|
|
||
| - Review the inferred contract before you rely on it in application code. | ||
| - Use the [PostgreSQL existing-project guide](/prisma-orm/add-to-existing-project/postgresql) for the first Prisma 8 query. | ||
| - Use the [full Prisma Postgres import guide](/prisma-postgres/import-from-existing-database-postgresql) for deeper migration details. | ||
| - [Migrate from Supabase to Prisma Postgres](/guides/switch-to-prisma-postgres/from-supabase) | ||
| - [Migrate from Neon to Prisma Postgres](/guides/switch-to-prisma-postgres/from-neon) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.