Move your PostgreSQL database from Zeabur to Zenavo: restore backups and verify data

Upload and restore a Zeabur PostgreSQL backup directly in Zenavo, understand how major versions, roles and pg_dumpall cluster dumps are handled, and verify your tables before switching traffic.

This post is also available in 简体中文, 繁體中文, 日本語, Español and Bahasa Indonesia.

When migrating from Zeabur, importing a template recreates your services in one step, while your database rows come across through a backup.

Zenavo natively accepts the PostgreSQL backup archive you download from Zeabur — no local unzipping or SQL editing required. This guide covers restoring a Zeabur PostgreSQL backup into Zenavo, checking your data, and cutting over your app. For the full project migration workflow, read this alongside our Zeabur migration guide.

1. Download the backup from Zeabur

Download a backup from your PostgreSQL service in Zeabur and note two things:

  • The PostgreSQL major version (such as 15, 16 or 17);
  • The database name your app actually uses.

The file Zeabur downloads is usually a .zip archive (even when its filename ends in .sql.gz, the outer container is often a ZIP) holding a pg_dumpall cluster SQL dump. Keep the downloaded file as-is — Zenavo accepts Zeabur's .zip backup archives directly, as well as plain .sql and .sql.gz dumps.

2. Create an empty PostgreSQL service on the same major version

In your target Zenavo project, click Add Service → Database → PostgreSQL, choose the same major version as on Zeabur, and deploy.

Three things to watch:

  • Match the major version first: Zenavo blocks restoring a cluster dump from a newer major version (such as PG 18) into an older one (such as PG 17). Move across on the same version first, and upgrade later if needed.
  • Keep the target database empty: don't start an app service that automatically runs migrations before you restore the backup. If the target database already has tables, a cluster restore will refuse to overwrite it.
  • Check extensions: if your database uses extensions such as PostGIS or pgvector, make sure the target image includes them.

3. Upload and restore the backup

Open the Backup & Restore tab on your new PostgreSQL service, click upload, select the backup file you downloaded from Zeabur in step 1, and run the restore.

Because a Zeabur backup is a pg_dumpall cluster export, Zenavo automatically converts it during restore (see Uploading a backup):

  • Restores your application database into the target service's database;
  • Creates any application roles referenced in the dump, preserving their passwords and row-level security (RLS) policies;
  • Demotes Zeabur's old root superuser role to a non-superuser, no-login role for safety while keeping its table and object ownership intact;
  • Skips the template1 and postgres system databases.

If a cluster dump contains more than one non-system application database, Zenavo will ask you to split them first. Nearly all Zeabur projects have a single application database and restore directly.

If a restore fails midway (for example, because of a missing extension), delete the half-restored PostgreSQL service and create a fresh empty one before retrying rather than restoring over partial tables.

4. Verify tables and row counts

Once the restore finishes, open the PostgreSQL service's Database View to run read-only SQL checks right in the browser.

First check that all your application tables are present:

SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
  AND table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY table_schema, table_name;

Then pick a few key tables (such as users or orders) and compare exact row counts and the latest timestamp against Zeabur:

SELECT count(*) AS row_count, max(created_at) AS latest_created_at
FROM public.orders;

(Use count(*) rather than estimated table statistics so you get the exact count.)

5. Connect your app and cut over

In your application service in the same project, point your database connection variable at the new PostgreSQL service (when the database service is named postgresql):

DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}

Don't hard-code Zeabur's old root username — use the connection variable exported by the new Zenavo database service (which also gives you a fresh password, important after Zeabur's 2026 security incidents).

For a zero-data-loss cutover, follow this sequence:

  1. Do a dry-run restore with a current backup on Zenavo and verify your app can sign in, query and create records;
  2. During a quiet window, pause the app on Zeabur (plus any cron jobs or queue workers) so no new writes land in the old database;
  3. Take one final backup on Zeabur and restore it into a clean PostgreSQL service on Zenavo;
  4. Start the app on Zenavo and switch your DNS records.

FAQ

The restore fails saying the target database is not empty? Your app service likely connected to the new database and ran auto-migrations (such as Prisma or TypeORM) before you restored the backup. Stop the app service, recreate a clean PostgreSQL service, restore the backup first, and then start the app.

The restore succeeded, but my app gets permission denied or can't log in? Zeabur's old root superuser is restored with login disabled. Update your app's DATABASE_URL to use ${POSTGRESQL_CONNECTION_STRING} from the Zenavo PostgreSQL service (which connects as postgres with the new password).

The file from Zeabur ends in .sql.gz and won't gunzip? That's a quirk of how Zeabur names its downloads — the file is actually a .zip archive. Don't rename or unpack it; upload it directly in Zenavo's Backup & Restore tab and Zenavo will detect the format automatically.

Once your database is restored and verified, enable scheduled backups in Backup & Restore. For the rest of the migration steps, see the full Zeabur migration guide.