Skip to content

From a self-hosted server

Tested with: zb migrate 0.1 · copy on PostgreSQL 15-17 and MySQL 8.0/8.4 · sync on PostgreSQL 17 and MySQL 8.4

Any server you run yourself can move into the matching Databasezy engine. If the server is reachable from the internet, give Databasezy a connection string; if it is behind a firewall, run the dump from a machine that can reach it.

Self-hosted server migration at a glance, from the migration source catalogue
Status Available
Target engines PostgreSQL , TimescaleDB , MySQL , MariaDB , Valkey , Redis (compat) , MongoDB (Percona Server) , FerretDB (MongoDB-compatible) , ClickHouse , InfluxDB 3 , QuestDB , Meilisearch , Qdrant , Weaviate , CouchDB , TypeDB
How we read it Connection string, Local tools (zb migrate --from local)
Continuous sync Yes, for PostgreSQL and MySQL
Provider preset None yet (generic)
  • A Databasezy instance of the matching engine, or use --create.
  • A user with read access to everything you migrate (and replication rights for sync).
  • Either network access from the egress addresses, or the engine’s dump tool on a machine that can reach the server.

Build a URL from the server’s host, port, user, password and database, in the scheme your engine uses (postgres://, mysql://, redis:// or rediss://, mongodb://). Turn on TLS on the server if you can; the migration uses it when offered.

  • Servers behind a firewall can be migrated with zb migrate --from local, which needs no inbound port (Postgres, TimescaleDB, MySQL, MariaDB, Redis, Valkey and MongoDB).
  • ClickHouse is copied table by table (SHOW CREATE TABLE, then SELECT ... FORMAT Native); Replicated and ClickHouse Cloud Shared table engines become MergeTree.
  • TypeDB needs the database named in the URL (typedb://user:password@host:1729/<database>) and TypeDB 3.11 or newer on both sides (the migration's TypeDB Console 3.13 and servers before 3.11 refuse each other).

Behind a firewall: zb migrate --from local runs the engine’s own dump tool on your machine and streams the result to a pre-signed upload URL. Nothing connects to your network from outside, but there is no continuous sync.

shell
PGHOST=db.internal PGUSER=migrator PGPASSWORD=pw \
zb migrate --from local --engine postgres --db app --to inst_abc
  1. Open the target PostgreSQL instance (create one first if you need to) and choose the Migrate in tab.
  2. Under Source, pick Connection string and paste the URL.
  3. Choose Copy and sync to keep the target current until cutover, or Copy for a one-off copy.
  4. Run Preflight and work through the checklist. Blocking items must be fixed before the copy starts.
  5. Start the copy and follow Copy and Verify. Mismatches are listed per table or collection.
  6. When the sync lag is low, stop writes and choose Cut over. The Report step keeps the timings and verification results.

PostgreSQL sync: set wal_level = logical (restart required), allow the migration user in pg_hba.conf, and give it REPLICATION. MySQL sync: binlog_format=ROW, gtid_mode=ON, enforce_gtid_consistency=ON, and a user with REPLICATION SLAVE, REPLICATION CLIENT and read access. Other engines are copy only.

Verification compares what the engine exposes: row counts per table for SQL engines, document counts per collection for document engines, key counts for key-value engines. Mismatches are listed in zb migrate status <mig_id> and the Verify step. Run your application’s tests against the new instance before you switch traffic.

With copy and sync, the target keeps receiving changes after the copy. When zb migrate status reports ready_for_cutover (two lag samples in a row under 5 seconds):

  1. Stop writes to the source: maintenance mode, scale writers to zero, or revoke the application user.
  2. Run zb migrate cutover <mig_id> (or Cut over in the portal). The agent drains the last changes, copies sequence values (PostgreSQL), verifies again and stops replication. If the target cannot catch up in time, the cutover is abandoned and sync keeps running; nothing changes on either side.
  3. Point the application at the Databasezy connection string and resume writes. zb migrate status shows the measured downtime.

Schema changes are not replicated, so freeze migrations between the copy and cutover. Details: what happens at cutover.

Sync applies to PostgreSQL and MySQL targets only; other engines from this source are copy only.

A migration never deletes anything on the source. Until you decommission it, rolling back means pointing the application back at the source.

  • Before cutover: cancel with zb migrate cancel <mig_id>. The publication, replication slot or replication channel the migration created is removed.
  • After cutover: writes made on Databasezy are not on the source. For PostgreSQL, start the migration with --reverse-sync so the old database keeps receiving them after cutover and stays a valid fallback. Without it, copy the new data back with a migration in the other direction before you switch.

Check that the source accepts connections from the egress addresses preflight prints, that the host is the public one and that the password has not been rotated. zb migrate --dry-run shows the exact URL that will be used after any rewrites.

Each item has a machine code and a fix. Nothing is written to the target until every blocking item is resolved; warnings do not block.

The source uses an extension Databasezy does not ship. Drop it on the source if it is unused, or exclude the objects that depend on it with --exclude.

Sync is blocked with pg.wal_level or pg.replica_identity

Section titled “Sync is blocked with pg.wal_level or pg.replica_identity”

Logical replication needs wal_level=logical on the source and a primary key (or REPLICA IDENTITY FULL) on every published table. Preflight lists the tables.

Sync is blocked on binlog or GTID settings

Section titled “Sync is blocked on binlog or GTID settings”

Continuous sync needs binlog_format=ROW, gtid_mode=ON, enforce_gtid_consistency=ON and a user with REPLICATION SLAVE and REPLICATION CLIENT. Preflight names whichever is missing.

For anything else, zb migrate status <mig_id> shows the failing step and its message. How migrations work describes every step.