34.6 pg_basebackup: Creating a Base Backup
Right, let’s talk about pg_basebackup. This is the workhorse, the trusty mule of PostgreSQL physical backups. It’s not glamorous, but when the proverbial fan gets clogged, this is the tool you’ll be desperately glad you set up correctly. In essence, pg_basebackup does one thing and does it well: it connects to a running PostgreSQL server and pulls a complete, bit-for-bit copy of the entire data directory (and optionally, the WAL archive) to create a perfect physical base backup. This is the literal foundation of any Point-in-Time Recovery (PITR) strategy. You can’t do PITR without one of these.
Now, the first thing you need to understand is that pg_basebackup is not some fancy file-copying script. It uses the replication protocol under the hood. This is a crucial distinction. It means the server knows a backup is happening and will manage the process cleanly. It’s the difference between sneaking a photo of a document and asking the archivist to provide you with a certified copy.
The Absolute Minimum Viable Command
Let’s get a backup. The simplest form requires just a target directory and a way to connect. You’ll need a user with the REPLICATION privilege (or be the almighty postgres superuser, but we’re not animals, we use least privilege).
pg_basebackup -D /var/lib/postgresql/backups/basebackup_$(date +%Y%m%d) -h primary-server -U replication_user
This will connect to primary-server and stream the entire data cluster into the specified directory. It will look like it’s hung while it copies—this is normal. Go get a coffee. The result will be a complete data directory. You could theoretically stop the PostgreSQL service on another machine, plop these files in place, start it up, and it would be a perfect replica of the source at the moment the backup started.
Why You Must Use -X stream
The -X flag controls how transaction log (WAL) information is handled during the backup. This is non-optional. You have two main choices, and one of them is a trap.
-X fetch: This is the bad idea. The backup process will periodically pause, copy any new WAL files that have been filled, and then resume. This creates start-stop I/O and, worse, it requireswal_keep_segmentsto be set high enough to prevent the server from recycling the old WAL files before the backup can fetch them. If the server deletes a needed WAL file, your backup aborts. It’s a race condition waiting to happen.-X stream: This is the correct choice. It opens a second connection alongside the data connection and streams the WAL records as they are generated, in real-time. This creates a consistent backup without the awful pausing and without relying onwal_keep_segments. The backup is consistent to the exact moment it finishes, not when it starts.
Always, always use -X stream. There is no good reason not to.
pg_basebackup -D ./my_backup -h primary-server -U rep_user -X stream
PITR is Useless Without the WALs
A base backup by itself is a snapshot. To get to any point in time after that snapshot, you need the continuous chain of WAL files generated after the backup ended. pg_basebackup can help you with this, too, using the -Z flag.
The -Z flag is a bit of a misnomer. It doesn’t “compress the backup” in a single action. What it actually does is tell pg_basebackup to also copy over the entire pg_wal directory (everything that was in it at the start of the backup) and then, crucially, it will stream all subsequent WAL records needed to make the backup consistent until the end of the process. It places these into a backup_manifest file and, more importantly, into a pg_wal directory within your backup location.
If you’re planning for PITR, you should be using this. It bundles the starting WAL files with your backup, which is incredibly convenient.
pg_basebackup -D ./my_pitr_backup -h primary-server -U rep_user -X stream -Z 9
The 9 after -Z is the compression level for the tar format (if you use -F tar), but it’s a no-op if you’re using the default -F plain format. The real magic is the WAL inclusion.
The Format Fandango: Plain vs. Tar
The -F flag chooses your output format. This isn’t about aesthetics; it’s about practicality.
-F plain(the default): This creates an exact copy of the server’s data directory on your filesystem. It’s ready to go. You canchownit topostgres:postgresand use it as a new data directory immediately. This is my preferred method for clarity and simplicity.-F tar: This writes the backup as a set of compressed tar files (.tar.gz), one per tablespace plus one for the base data. The main “advantage” is that it can be easier to move around, but you then have to untar it to use it, which just adds a step. I find it mostly annoying unless you’re shipping backups directly to object storage.
Best Practices and Pitfalls
- Monitor Your Backups: This thing can run for hours on a large cluster. Don’t just fire and forget. Use
-vfor verbose mode or, better yet, wrap it in a script that logs output and sends you an email on success/failure. A backup that failed silently is worse than no backup at all—it gives you a false sense of security. - Mind the Space: A
pg_basebackupwill roughly double your disk usage while it runs. The tool first copies everything to a temporary directory (with a.pidsuffix) and only renames it to the target directory upon successful completion. This prevents you from ending up with a half-baked backup, but it means you need enough free space for two complete copies of your database. Check your disk. Seriously. - Network is Part of Your Backup System: Since this streams over the network, a flaky connection will kill your backup. If you’re backing up a multi-terabyte database across a congested WAN link, you might want to look at other methods or ensure you have a robust, stable connection.
- It’s a Point-in-Time Snapshot, Not a Continuous Backup: This is a common point of confusion.
pg_basebackupgives you a single, frozen snapshot. To recover to a point after that snapshot, you must have been continuously archiving WAL files using PostgreSQL’sarchive_commandsetting.pg_basebackupis one half of the PITR duo. It’s useless without its partner, the WAL archive.