Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Import from PostgreSQL

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 ORM 8, Prisma ORM 7, earlier Prisma ORM versions, and standard PostgreSQL clients. Moving your database does not require changing your ORM version.

You need:

Confirm that all three tools use PostgreSQL 17:

Bash
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.

Set the direct source connection URL. Keep the single quotes so your shell does not interpret special characters in the URL:

Bash
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
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
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. 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
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.

Create a new Prisma Postgres database in Prisma Console:

  1. Open your project and select the database.
  2. Click Connect to your database.
  3. Click Generate new connection string.
  4. Copy both the direct and pooled connection strings.

Set the direct connection string for the import:

Bash
export PRISMA_POSTGRES_DIRECT_URL='postgres://USER:PASSWORD@db.prisma.io:5432/postgres?sslmode=require'

Confirm that the target is reachable and empty:

Bash
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.

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 for the other options:

Bash
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
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
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.

Restore the archive through the direct Prisma Postgres connection:

Bash
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
psql "$PRISMA_POSTGRES_DIRECT_URL" \

  -X \

  --set ON_ERROR_STOP=1 \

  --command="ANALYZE;"

Create a reusable query that returns an exact row count for every non-system table:

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
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.

Choose the path that matches your application:

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.

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.

Set the pooled URL for runtime queries and the direct URL for Prisma CLI commands:

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:

Bash
bunx prisma db verify --db "$DIRECT_URL"
Terminal
pnpm prisma db verify --db "$DIRECT_URL"
Terminal
yarn prisma db verify --db "$DIRECT_URL"
Terminal
npx prisma 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.

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.

Set the pooled URL for runtime queries and the direct URL for Prisma CLI commands:

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 ORM 7 reads the CLI connection from datasource.url in prisma.config.ts. Point it at DIRECT_URL as shown in the Prisma ORM 7 PostgreSQL setup. Prisma ORM 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.

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:

Bash
bun remove @prisma/extension-accelerate
Terminal
pnpm remove @prisma/extension-accelerate
Terminal
yarn remove @prisma/extension-accelerate
Terminal
npm uninstall @prisma/extension-accelerate

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 ORM 7, connect through the PostgreSQL driver adapter. For Prisma ORM 6 or earlier, use Prisma Client with its bundled query engine or a driver adapter your version supports.

Accelerate reached edge runtimes over HTTP. For an edge or TCP-constrained runtime, use the Prisma Postgres serverless driver. For a conventional Node.js or Bun runtime, use the Prisma Postgres pooled TCP connection.

Regenerate Prisma Client:

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 ORM 7, connect through the PostgreSQL driver adapter. For Prisma ORM 6 or earlier, use Prisma Client with its bundled query engine or a driver adapter your version supports.

Accelerate reached edge runtimes over HTTP. For an edge or TCP-constrained runtime, use the Prisma Postgres serverless driver. For a conventional Node.js or Bun runtime, use the Prisma Postgres pooled TCP connection.

Regenerate Prisma Client:

bun
bunx prisma generate
pnpm
pnpm prisma generate
yarn
yarn prisma generate
npm
npx prisma generate

Search your project for @prisma/extension-accelerate and prisma://. Neither should appear.

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:

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"

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.

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.

After you have verified the application and retained any audit evidence you need, delete the local dump and row-count files:

Bash
rm postgres-to-prisma-postgres.dump postgres-restore.list row-counts.sql source-row-counts.txt target-row-counts.txt

Your database migration is complete. Prisma ORM 8 is the current release, but upgrading is a separate application change. Follow the incremental Prisma ORM 7 to 8 guide 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.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu