How to Benchmark PostgreSQL wal_compression: Comparing off, pglz, lz4, and zstd

Write-Ahead Logging is one of the core reliability features of PostgreSQL. Every change made to data is first recorded in WAL so that the database can recover after crashes and support replication.

When workloads generate many full-page writes, WAL volume can grow quickly. PostgreSQL provides the wal_compression setting to reduce that overhead by compressing full-page images written into WAL.

Modern PostgreSQL builds support multiple compression methods:

  • off
  • pglz
  • lz4
  • zstd

This article explains how to benchmark these methods using a real workload and includes measured results from a 500,000-row update test.

What Is wal_compression?

The wal_compression parameter controls whether PostgreSQL compresses full-page images before writing them to WAL.

You can check the active value with:

SHOW wal_compression;

Result :

 wal_compression 
-----------------
 off
(1 row)

You can inspect more metadata of wal_compression like this:

SELECT *
FROM pg_settings
WHERE name = 'wal_compression';

Result :

name            | wal_compression
setting         | off
unit            | 
category        | Write-Ahead Log / Settings
short_desc      | Compresses full-page writes written in WAL file with specified method.
extra_desc      | 
context         | superuser
vartype         | enum
source          | configuration file
min_val         | 
max_val         | 
enumvals        | {pglz,lz4,zstd,on,off}
boot_val        | off
reset_val       | zstd
sourcefile      | /etc/postgresql/18/main/postgresql.conf
sourceline      | 247
pending_restart | f

Typical supported values:

  • off
  • pglz
  • lz4
  • zstd

Why Benchmark It?

Choosing the right WAL compression method can affect:

  • Update speed
  • WAL bytes generated
  • Replication bandwidth
  • Archive storage usage
  • Overall write performance

Compression adds CPU work, but it can reduce disk I/O significantly. The best choice depends on the balance between CPU and storage performance.

Test Workload Used

The benchmark was executed on a database named walbench using a table with 500,000 rows.

Each test performed a full-table update on a large text column.

Create the Benchmark Table

DROP TABLE IF EXISTS wal_test;
CREATE TABLE wal_test (
    id serial PRIMARY KEY,
    data text
);

Insert 500,000 Rows

INSERT INTO wal_test(data)
SELECT repeat(md5(random()::text), 50)
FROM generate_series(1, 500000);

This creates a large enough dataset to generate meaningful WAL traffic.

Benchmark Method

For each compression method, the same sequence was used:

  1. Confirm the compression mode
  2. Run CHECKPOINT
  3. Capture WAL statistics
  4. Capture starting LSN
  5. Run the update
  6. Capture ending LSN
  7. Capture WAL statistics again
  8. Compare runtime and WAL generated

Queries Used for Every Test

SHOW wal_compression;
CHECKPOINT;
SELECT * FROM pg_stat_wal;
SELECT pg_current_wal_lsn();
UPDATE wal_test
SET data = repeat(md5(random()::text), 50);
SELECT * FROM pg_stat_wal;
SELECT pg_current_wal_lsn();
SELECT pg_wal_lsn_diff('END_LSN', 'START_LSN');

Test Result: wal_compression = off

The database generated the largest amount of WAL and had the slowest runtime.

walbench=# show wal_compression;
 wal_compression 
-----------------
 off
(1 row)
Time: 0.343 ms

walbench=# checkpoint;
CHECKPOINT
Time: 11.539 ms

walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 35/71882270
(1 row)
Time: 0.442 ms

walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    21744457 | 3204988 | 16282412215 |          1372404 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.489 ms

walbench=# UPDATE wal_test
SET data = repeat(md5(random()::text), 50);
UPDATE 500000
Time: 17125.486 ms (00:17.125)

walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    23244564 | 3334064 | 18014676487 |          1551719 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.295 ms

walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 36/5E3C700
(1 row)
Time: 0.272 ms

walbench=# SELECT pg_wal_lsn_diff('36/5E3C700', '35/71882270');
 pg_wal_lsn_diff 
-----------------
      2489033872
(1 row)
Measured results:
  • Execution time: 17.125 seconds
  • LSN WAL generated: 2,489,033,872 bytes
  • pg_stat_wal bytes increase: 1,732,264,272 bytes

This mode produced the highest WAL overhead and the slowest update performance.

Test Result: wal_compression = pglz

Now change the value of the wal_compression like this

Check the path of the postgres conf file like this

show config_file ;

Result :

               config_file               
-----------------------------------------
 /etc/postgresql/18/main/postgresql.conf
(1 row)

Edit that file

sudo nano /etc/postgresql/18/main/postgresql.conf

Change value like this 

wal_compression = on			# enables compression of full-page writes;

This built-in compression method reduced WAL volume and improved execution time.

walbench=# show wal_compression ;
 wal_compression 
-----------------
 pglz
(1 row)
Time: 0.174 ms
walbench=# checkpoint ;
sCHECKPOINT
Time: 393.790 ms
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    27318177 | 4642270 | 21905768889 |          1666304 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.368 ms
walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 36/C43BBF38
(1 row)
Time: 0.324 ms
walbench=#              
UPDATE wal_test
SET data = repeat(md5(random()::text), 50);
UPDATE 500000
Time: 10869.771 ms (00:10.870)
walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 36/FCCC28F0
(1 row)
Time: 0.335 ms
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    28928285 | 4791274 | 22872241039 |          1696488 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.360 ms
walbench=# SELECT pg_wal_lsn_diff('36/FCCC28F0', '36/C43BBF38');
 pg_wal_lsn_diff 
-----------------
       948988344
(1 row)
Time: 0.413 ms

Measured results:

  • Execution time: 10.870 seconds
  • LSN WAL generated: 948,988,344 bytes
  • pg_stat_wal bytes increase: 966,472,150 bytes

This was a major improvement over off.

Test Result: wal_compression = lz4

Now change the value of the wal_compression like this

Check the path of the postgres conf file like this

show config_file ;

Result :

               config_file               
-----------------------------------------
 /etc/postgresql/18/main/postgresql.conf
(1 row)

Edit that file

sudo nano /etc/postgresql/18/main/postgresql.conf

Change value like this

wal_compression = lz4			# enables compression of full-page writes;

This mode delivered the fastest runtime in the benchmark.

walbench=# show wal_compression;
 wal_compression 
-----------------
 lz4
(1 row)
Time: 0.255 ms
walbench=# checkpoint;
CHECKPOINT
Time: 10.248 ms
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    29637839 | 4930637 | 22963065382 |          1696488 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.732 ms
walbench=#  SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 37/2638728
(1 row)
Time: 0.283 ms
walbench=# UPDATE wal_test
SET data = repeat(md5(random()::text), 50);
UPDATE 500000
Time: 7530.834 ms (00:07.531)
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    31139239 | 5058406 | 23907451856 |          1804991 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.449 ms
walbench=#  SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 37/3B1BC528
(1 row)
Time: 0.271 ms
walbench=# SELECT pg_wal_lsn_diff('37/3B1BC528', '37/2638728');
 pg_wal_lsn_diff 
-----------------
       951598592
(1 row)
Time: 0.438 ms

Measured results:

  • Execution time: 7.531 seconds
  • LSN WAL generated: 951,598,592 bytes
  • pg_stat_wal bytes increase: 944,386,474 bytes

This mode combined excellent speed with low WAL generation.

Test Result: wal_compression = zstd

Now change the value of the wal_compression like this

Check the path of the postgres conf file like this

show config_file ;

Result :

               config_file               ----------------------------------------- /etc/postgresql/18/main/postgresql.conf(1 row)

Edit that file

sudo nano /etc/postgresql/18/main/postgresql.conf

Change value like this

walbench=# show wal_compression ;
 wal_compression 
-----------------
 zstd
(1 row)
Time: 0.156 ms
walbench=# checkpoint ;
CHECKPOINT
Time: 189.495 ms
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    31473600 | 5435756 | 24016797437 |          1805319 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.411 ms
walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 37/42935C68
(1 row)
Time: 0.347 ms
walbench=# UPDATE wal_test
SET data = repeat(md5(random()::text), 50);
UPDATE 500000
Time: 9306.786 ms (00:09.307)
walbench=# select * from pg_stat_wal;
 wal_records | wal_fpi |  wal_bytes  | wal_buffers_full |           stats_reset            
-------------+---------+-------------+------------------+----------------------------------
    32980468 | 5494169 | 24939172650 |          1840121 | 2026-04-22 13:47:27.486327+05:30
(1 row)
Time: 0.368 ms
walbench=# SELECT pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 37/7AFF8000
(1 row)
Time: 0.246 ms
walbench=# SELECT pg_wal_lsn_diff('37/7AFF8000', '37/42935C68');
 pg_wal_lsn_diff 
-----------------
       946611096
(1 row)
Time: 0.426 ms

Measured results:

  • Execution time: 9.307 seconds
  • LSN WAL generated: 946,611,096 bytes
  • pg_stat_wal bytes increase: 922,375,213 bytes

This mode achieved the best compression result in the benchmark.

Using the latest benchmark run:

  • off was the slowest and generated the most WAL.
  • pglz was much better than off and significantly reduced WAL overhead.
  • lz4 was the fastest option overall.
  • zstd produced the smallest WAL volume.

Choose lz4 if You Need Maximum Speed

Best for:

  • OLTP systems
  • Frequent updates
  • High transaction workloads
  • Low-latency write performance

Based on this benchmark, lz4 was the best overall performance option.

Choose zstd if You Need Best Compression

Best for:

  • WAL archiving
  • Replication over limited bandwidth
  • Storage-sensitive systems

It slightly trailed lz4 in speed but generated the least WAL.

Choose pglz for Compatibility

Best for:

  • Older environments
  • Systems without external compression libraries

It still performed well and clearly beat off.

Avoid off for Heavy Update Workloads

Without compression, PostgreSQL wrote much more WAL data and needed more time to finish the same update.

Why Compression Improved Speed

Many users expect compression to slow down writes because it uses CPU cycles. In practice, storage I/O is often the bigger bottleneck.

By reducing WAL volume, PostgreSQL writes fewer bytes to disk. That lower I/O cost can outweigh compression overhead, leading to faster total execution time.

That is exactly what happened in this benchmark.

Recommended Configuration

For speed-focused systems:

ALTER SYSTEM SET wal_compression = 'lz4';
SELECT pg_reload_conf();

For storage-focused systems:

ALTER SYSTEM SET wal_compression = 'zstd';
SELECT pg_reload_conf();

For built-in compatibility:

ALTER SYSTEM SET wal_compression = 'pglz';
SELECT pg_reload_conf();

How to Reproduce This Benchmark Yourself

  1. Create a large test table
  2. Insert enough rows
  3. Change wal_compression
  4. Run CHECKPOINT
  5. Execute identical updates
  6. Measure runtime
  7. Compare pg_stat_wal
  8. Compare pg_wal_lsn_diff()

Always test on your own workload before changing production settings.

This benchmark shows that WAL compression is not only about saving storage. It can also improve performance.

From the measured results:

  • lz4 was the fastest
  • zstd generated the least WAL
  • pglz was a strong middle option
  • off performed worst under the same workload

For modern PostgreSQL systems, enabling wal_compression with lz4 or zstd is often a better choice than leaving it disabled.

whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message