Backups and Durability10 min read

MySQL Backups vs PostgreSQL Backups: Tools, PITR, and Restores

mysqldump, XtraBackup, and the clone plugin map surprisingly well onto pg_dump, pg_basebackup, and pgBackRest. The concepts transfer; the tools, PITR mechanics, and failure modes do not.

Backup strategy is the part of a MySQL-to-PostgreSQL migration where the concepts transfer almost perfectly and the tools transfer not at all. Both ecosystems distinguish logical dumps from physical backups, both do point-in-time recovery by replaying a change stream on top of a base backup, and both punish teams that never test restores. But every command is different, the PITR mechanics differ in important details, and a couple of MySQL conveniences have no direct PostgreSQL equivalent. Having carried pagers for both, here is the mapping I give teams before cutover, because the worst time to learn a new backup stack is during your first PostgreSQL data-loss event.

The MySQL toolbox, briefly and fairly

The classic layer is mysqldump: logical, single-threaded SQL output, consistent for InnoDB with --single-transaction, fine for small databases and schema exports, painfully slow to restore at scale because the server replays every INSERT and rebuilds every index. MySQL Shell's dump utilities (util.dumpInstance and util.loadDump) modernized this with parallel dump and load, compression, and chunking, and are now the recommended logical path for anything sizable.

The physical layer is Percona XtraBackup: hot physical backups of a running server by copying tablespace files while capturing redo, then a prepare step applies the log to make the copy consistent. It supports incrementals, compression, streaming, and encryption, and it has been the backbone of self-managed MySQL backup infrastructure for years. One version note that has bitten real teams: XtraBackup must track the server version closely, and redo format changes across MySQL 8.x releases mean an old XtraBackup cannot back up a newer server.

MySQL 8.0.17 added the clone plugin, which the receiving server can drive itself to pull a full physical snapshot from a donor, either onto local disk or directly into the recipient's own data directory.

INSTALL PLUGIN clone SONAME 'mysql_clone.so';
SET GLOBAL clone_valid_donor_list = 'source-host:3306';
CLONE INSTANCE FROM 'clone_user'@'source-host':3306
  IDENTIFIED BY 'clone_password';

Clone is primarily a provisioning tool, rebuilding replicas and spinning up new nodes fast, but plenty of teams use it as an operational snapshot mechanism too. PITR in MySQL rides on the binary log: restore a base backup, then replay binlogs with mysqlbinlog --stop-datetime up to the moment before the mistake. It works, with the standing caveats that binlog retention must be configured generously and that replaying statement-level history requires care around the exact GTID or position where the base backup ends.

The PostgreSQL toolbox

The logical layer maps cleanly. pg_dump produces a consistent logical dump of one database without blocking writers; the directory format with --jobs gives parallel dump, and pg_restore --jobs gives parallel restore, so the MySQL Shell utilities' role is covered by the built-in tools. One genuine trap for MySQL operators: pg_dump does not capture cluster-wide objects. Roles and tablespaces live outside any single database, so you need pg_dumpall --globals-only alongside per-database dumps, and forgetting it is the classic first-restore surprise: a perfect schema with no users to own it.

The physical layer is pg_basebackup: a streaming copy of the entire cluster taken over the replication protocol from a running server, no separate prepare step, usable directly to seed replicas or as a base for PITR. It is full-cluster only, no incrementals until the incremental support introduced in PostgreSQL 17, and single-purpose by design. Which is why production deployments standardize on pgBackRest (or Barman): parallel backup and restore, full, differential, and incremental backups, retention policies, encryption, direct-to-object-storage repositories, WAL archive management, and backup verification in one tool. The honest mapping for an XtraBackup shop is that pgBackRest is your new XtraBackup plus the scheduling and retention scripts you wrote around it.

PITR: binlog replay versus WAL replay

PostgreSQL's PITR is architecturally cleaner than MySQL's, and it is worth understanding why. The binary log is a logical change stream that exists for replication and gets replayed as SQL-level events on top of a restored server. PostgreSQL's write-ahead log is the physical redo stream the engine itself uses for crash recovery; PITR is just crash recovery pointed at an archive and told when to stop. You configure archive_mode and archive_command (or use pgBackRest's archive integration), take periodic base backups, and to recover you restore a base backup, point restore_command at the archive, and set recovery_target_time. The database replays WAL to that moment and promotes.

The operational risk concentrates in one place: the archive. If archiving silently fails, every base backup after that point cannot be rolled forward, and you will not find out until you try. The archiver's health is a first-class monitoring target.

SELECT archived_count,
       failed_count,
       last_archived_wal,
       last_archived_time,
       last_failed_wal,
       last_failed_time
FROM pg_stat_archiver;

A non-zero failed_count that keeps climbing, or a last_archived_time falling behind wall-clock time, is a page-worthy condition: it means your recovery point objective is quietly degrading while everything else looks green. The same discipline applies to replication slots kept for backup tooling, since an abandoned slot will retain WAL until the disk fills. If you run your own instances, the self-hosted PostgreSQL monitoring guide covers wiring these signals in from day one.

Pick both layers, in both worlds

The strategy conversation is identical across engines, which is comforting. Logical backups are portable across versions and architectures, restore selectively down to a single table, and double as migration tooling, but restores are slow because everything is replayed and rebuilt. Physical backups restore at disk speed and are the only realistic path for large databases and tight recovery-time objectives, but they are all-or-nothing and version-bound. Mature setups run both: pgBackRest for the real recovery path with PITR, plus periodic pg_dump of critical databases for portability, selective restore, and protection against subtle corruption propagating through physical copies. That is the same layered posture a well-run MySQL shop already has with XtraBackup plus scheduled dumps, so the reasoning transfers even though every command changes. What has no PostgreSQL equivalent is the clone plugin's pull-a-copy-into-myself workflow; provisioning a replica is instead pg_basebackup or a pgBackRest restore, which covers the same need with one more step.

Restore testing is the actual backup strategy

Everything above is machinery; this section is the part that determines whether you survive an incident. A backup that has never been restored is a hypothesis. The discipline I hold teams to, on either engine, is the same. Restore on a schedule, automatically: a job that takes the latest backup, restores it to a scratch instance, replays WAL to a recent point, and runs verification queries against known tables. Verify content, not just exit codes: row counts on critical tables, checksums where feasible, application smoke queries. Time it: the restore duration is your real recovery time objective, and it grows with the database while nobody watches. Test the PITR path specifically, not just the base restore, because recovery_target_time, timeline handling, and archive retrieval are exactly the parts that fail when unrehearsed. And test the awkward scenarios once a quarter: restore without the person who built the system, restore the globals, restore to a different major version via logical dump. Teams migrating from MySQL have one advantage here: cutover forces a full restore rehearsal by definition. Keep the habit after the migration instead of letting it decay. A broader treatment of operational readiness lives in the PostgreSQL overview.

How MonPG helps after you land on PostgreSQL

MonPG monitors PostgreSQL only, so it will not watch your remaining MySQL fleet, but on the PostgreSQL side it covers the observability half of backup safety: archiver health and WAL archive failures, replication slot retention that threatens to fill disks, WAL generation rate (which sets both your archive volume and your PITR replay time), long-running backups' impact on the workload, and disk headroom trends. None of that replaces pgBackRest or your restore rehearsals, and it does not try to; it makes sure the quiet failure modes, the silently failing archive_command, the forgotten slot, the WAL spike after a schema change, surface as alerts instead of as discoveries during a recovery. The PostgreSQL monitoring guide shows where these signals fit in the baseline every production cluster should have before it takes real traffic.