Point-in-Time Recovery (PITR) with pg_basebackup in PostgreSQL 18

 pg_basebackup is a useful tool for creating a base backup of a PostgreSQL cluster. When combined with continuous WAL archiving, it can be used to perform Point-in-Time Recovery (PITR).

1. Check the Current PostgreSQL Cluster

psql (18.0)
WARNING: Console code page (720) differs from Windows code page (1256)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
Type "help" for help.

postgres=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+----------------------------+----------------------------+--------+-----------+-----------------------
postgres | postgres | UTF8 | libc | English_United States.1256 | English_United States.1256 | | | =Tc/postgres +
| | | | | | | | postgres=CTc/postgres
template0 | postgres | UTF8 | libc | English_United States.1256 | English_United States.1256 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | English_United States.1256 | English_United States.1256 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
usefdb | postgres | UTF8 | libc | English_United States.1256 | English_United States.1256 | | |

2. Configure WAL Archiving

D:\postgres18_windows_installed\data\postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'copy "%p" "E:\\Archive\\%f"'
E:\Archive
C:\Users\Win10-991005> pg_ctl restart
waiting for server to shut down....
done
server stopped
waiting for server to start....2026-08-12 22:32:04 +0330 LOG: redirecting log output to logging collector process
2026-08-12 22:32:04 +0330 HINT: Future log output will appear in directory "log".
done
server started

3. Generate a WAL Segment

postgres=# select pg_switch_wal();
pg_switch_wal
---------------

0/1D0020C0
(1 row)
C:\Windows\system32>dir e:\Archive\*
08/12/2026 10:32 PM <DIR> .
08/12/2026 10:32 PM <DIR> ..
08/12/2026 10:32 PM 16,777,216 00000005000000000000001D
1 File(s) 16,777,216 bytes
2 Dir(s) 7,594,000,384 bytes free

4. Create a Base Backup with pg_basebackup

C:\Users\Win10-991005> pg_basebackup -D "E:\backup" -Ft -P -U postgres
Password:

32844/32844 kB (100%), 1/1 tablespace
C:\Windows\system32>dir E:\backup\*
08/12/2026 10:34 PM 184,439 backup_manifest
08/12/2026 10:34 PM 33,632,768 base.tar
08/12/2026 10:34 PM 33,558,528 pg_wal.tar
3 File(s) 67,375,735 bytes
2 Dir(s) 7,476,281,344 bytes free

5. Generate Some Test Data

6. Determine the PITR Target Time

Download the Medium app
postgres=# select pg_switch_wal();
pg_switch_wal
---------------

0/214ADFF0
(1 row)
postgres=# select now();
now
----------------------------------

2026-08-12 22:36:26.779349+03:30
(1 row)
postgres=# insert into tb_2026_08_12 values(2,'Payan Rafat');
INSERT 0 1
postgres=# select pg_switch_wal();
pg_switch_wal
---------------

0/22000160
(1 row)

7. Prepare the Environment for Recovery

C:\Users\Win10-991005>pg_ctl  stop
waiting for server to shut down.... 1 file(s) copied.
.. done
server stopped
C:\Users\Win10-991005>rmdir /s /q "D:\postgres18_windows_installed\data"
C:\Users\Win10-991005>mkdir "D:\postgres18_windows_installed\data" 

8. Restore the Base Backup

C:\Users\Win10-991005>tar -xvf "E:\backup\base.tar" -C "D:\postgres18_windows_installed\data"

9. Create recovery.signal

C:\Users\Win10-991005> type nul > "D:\postgres18_windows_installed\data\recovery.signal"

10. Configure PITR

restore_command = 'copy "E:\\Archive\\%f" "%p"'

recovery_target_time = '2026-08-12 22:36:26'

recovery_target_action = 'promote'

11. Start PostgreSQL and Perform PITR

C:\Users\Win10-991005> pg_ctl start
waiting for server to start....2026-08-12 22:47:03 +0330 LOG: redirecting log output to logging collector process
2026-08-12 22:47:03 +0330 HINT: Future log output will appear in directory "log".
done
server started
2026-08-12 22:47:03 +0330 LOG:  starting PostgreSQL 18.0 on x86_64-windows, compiled by msvc-19.44.35217, 64-bit
2026-08-12 22:47:03 +0330 LOG: listening on IPv6 address "::", port 5432
2026-08-12 22:47:03 +0330 LOG: listening on IPv4 address "0.0.0.0", port 5432
2026-08-12 22:47:03 +0330 LOG: database system was interrupted; last known up at 2026-08-12 22:34:34 +0330
2026-08-12 22:47:06 +0330 LOG: starting backup recovery with redo LSN 0/20000028, checkpoint LSN 0/20000080, on timeline ID 5
2026-08-12 22:47:06 +0330 LOG: restored log file "000000050000000000000020" from archive
2026-08-12 22:47:06 +0330 LOG: starting point-in-time recovery to 2026-08-12 22:36:26+03:30
2026-08-12 22:47:06 +0330 LOG: redo starts at 0/20000028
2026-08-12 22:47:06 +0330 LOG: completed backup recovery with redo LSN 0/20000028 and end LSN 0/20000120
2026-08-12 22:47:06 +0330 LOG: consistent recovery state reached at 0/20000120
2026-08-12 22:47:06 +0330 LOG: database system is ready to accept read-only connections
2026-08-12 22:47:06 +0330 LOG: restored log file "000000050000000000000021" from archive
2026-08-12 22:47:06 +0330 LOG: restored log file "000000050000000000000022" from archive
2026-08-12 22:47:06 +0330 LOG: recovery stopping before commit of transaction 850, time 2026-08-12 22:36:53.547442+03:30
2026-08-12 22:47:06 +0330 LOG: redo done at 0/220000E8 system usage: CPU: user: 0.01 s, system: 0.07 s, elapsed: 0.36 s
2026-08-12 22:47:06 +0330 LOG: last completed transaction was at log time 2026-08-12 22:36:02.679092+03:30
2026-08-12 22:47:06 +0330 LOG: selected new timeline ID: 6
2026-08-12 22:47:06 +0330 LOG: archive recovery complete
2026-08-12 22:47:06 +0330 LOG: checkpoint starting: end-of-recovery immediate wait
2026-08-12 22:47:08 +0330 LOG: checkpoint complete: wrote 1024 buffers (5.6%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 2 recycled; write=0.486 s, sync=0.968 s, total=1.499 s; sync files=335, longest=0.009 s, average=0.003 s; distance=32768 kB, estimate=32768 kB; lsn=0/220000E8, redo lsn=0/220000E8
2026-08-12 22:47:08 +0330 LOG: database system is ready to accept connections
LOG:  database system is ready to accept connections

12. Verify the Recovery

postgres=#  select * from tb_2026_08_12;
id | name
----+-------------------
1 | Vahid Yousefzadeh
(1 row)
Press enter or click to view image in full size
Only the first row exists.
2 | Payan Rafat

Comments

Popular posts from this blog

Oracle 21c Enhancements for TTS Export/Import

Oracle 23ai — error_message_details Parameter for Displaying Error Details

Buffer Busy Wait and Read by Other Session in Oracle