The Compression Triangle Nobody Wanted
For most of the 2010s, Linux compression was a depressing trade-off between three bad options.
gzip, Fast enough to not hate your life, but the compression ratio is basically “well, it’s smaller than before.” In our measurements gzip -6 shrank a 256 MiB SQL dump to about 20% of its size and a container image to about 36%, and that’s the low end of the modern field. The upside: every system on Earth has gunzip. You will never be stranded.
bzip2, Better ratio than gzip. Also 2 to 11 times slower to compress in our tests, and the slowest of the bunch to decompress. A bzip2 backup job on a big dataset is the kind of thing you start before lunch and check on after your afternoon standup. If the ratio gain was worth the wait… honestly, usually it wasn’t.
xz, The nuclear option. Excellent compression ratios. Also painfully slow at its high levels. For years it ran on one core, CPU-maxed, “maybe tomorrow” territory for large datasets (xz gained a -T threads option in 5.2.0 in 2014, and only made it the default in 5.6.0 in 2024). You used it for distribution tarballs and release artifacts, things you compress once and decompress occasionally. Never for anything time-sensitive.
Every choice was a compromise. Then 2016 happened.
Facebook Dropped a Gift on the World
Yann Collet at Facebook released Zstandard (zstd) 1.0 in 2016, and the pitch sounded too good: gzip speed or better, with much better ratios.
We later measured it. zstd vs lz4 vs gzip vs xz: Measured runs 19 configs on four public 256 MiB data sets (single thread, median of 3 runs, zstd 1.5.7, xz 5.8.4, gzip 1.14, bzip2 1.0.8). An earlier version of this post showed a benchmark table we could not trace to a test run, so we replaced it with these numbers:
| Single thread | Logs ratio | SQL dump ratio | Container image ratio | Compress speed (SQL dump) |
|---|---|---|---|---|
| gzip -6 | 12.5x | 5.11x | 2.80x | 53 MiB/s |
| bzip2 -9 | 20.5x | 6.40x | 3.12x | 13 MiB/s |
| zstd -3 | 16.2x | 5.22x | 3.00x | 228 MiB/s |
| zstd -19 | 24.0x | 6.94x | 3.82x | 1.9 MiB/s |
| xz -6 | 24.0x | 7.25x | 4.19x | 3.1 MiB/s |
zstd -3 is 4 to 6 times faster than gzip -6 to compress, decompresses faster too, and gets a better ratio on every compressible data set. bzip2 still gets a better ratio than zstd -3, but it pays for that with compression 13 to 45 times slower than zstd -3 and the slowest decompression in the test.
The high end surprised us. zstd -19 does not beat xz at its own game: single-threaded xz -6 was faster than zstd -19 on every compressible data set, and smaller on two of three. Where zstd wins is the restore. zstd -19 output decompressed 3.6 to 6.5 times faster than xz -6 output. You pay at compress time, not decompress time, which matters for anything you compress once and restore under pressure.
Who Already Made the Switch
You’re probably already using zstd without realizing it.
The Linux kernel can compress its modules with zstd (CONFIG_MODULE_COMPRESS_ZSTD, since 5.13) and can boot from a zstd-compressed kernel image.
Docker can push image layers as zstd (docker buildx build --output type=image,compression=zstd,...). It is opt-in: the buildx default is still gzip, because older registries and runtimes can’t read zstd layers.
Btrfs and ZFS both support zstd as transparent filesystem compression (Btrfs since kernel 4.14, OpenZFS since 2.0). zstd beats zlib on speed and ratio. ZFS still defaults to lz4, which compresses about 2 times faster than zstd -1 in our tests at a worse ratio, a fair trade for a filesystem that compresses every write.
Basic Usage, It’s Exactly What You’d Expect
# Compress a filezstd file.txt# Creates file.txt.zst, keeps original
# Decompressunzstd file.txt.zst# or: zstd -d file.txt.zst
# Compress in-place (remove original)zstd --rm file.txt
# Decompress to stdoutzstd -dc file.txt.zst | lessThe .zst extension. Clean, unambiguous. No legacy baggage.
Level Tuning, This Is Where It Gets Interesting
zstd has 22 compression levels. Default is 3.
# Default (level 3): faster than gzip, better ratiozstd file.tar
# Faster than default, slightly worse ratiozstd -1 file.tar
# Middle groundzstd -9 file.tar
# High compression (slower)zstd -19 file.tar
# Ludicrous modezstd --ultra -22 file.tarFor most backup and transfer use cases, level 3 is the right answer. You’re already beating gzip’s ratio at gzip’s speed. Levels 1-3 are everyday drivers. Levels 10-19 are for “I have time and I want the ratio.” Level 22 is for when you enjoy suffering slightly.
Parallel Compression, The Part bzip2 Wishes It Had
This is the one that changes the math on large datasets:
# Use all available CPU coreszstd -T0 bigarchive.tar
# Use 4 cores explicitlyzstd -T4 bigarchive.tar
# High compression + all coreszstd -19 -T0 massive.tar-T0 detects and uses the number of physical cores (add --auto-threads=logical to count hyperthreads too). In our tests, zstd -3 -T4 on four physical cores compressed about 4 times faster than one thread, with an identical ratio. bzip2’s parallel version (pbzip2) exists but it’s a separate binary and not universally available. xz has native threads too (xz -T0), and since xz 5.6.0 it uses them by default.
Every time I see someone running a bzip2 backup job in 2026 I want to send them this article.
tar + zstd, The Archive You Actually Want
tar got native zstd support in version 1.31 (2019). Most modern Linux systems have it.
# Create archivetar --zstd -cvf archive.tar.zst directory/
# Extracttar --zstd -xvf archive.tar.zst
# Alternative: pipe to zstd manually (works on older tar)tar -cvf - directory/ | zstd -T0 > archive.tar.zst
# Extract the manual wayzstd -dc archive.tar.zst | tar -xvf -
# Check your tar version supports --zstdtar --versionIf --zstd isn’t available on your system, the pipe approach works everywhere. The -I flag lets you specify the compression program:
tar -I zstd -cvf archive.tar.zst directory/tar -I "zstd -19 -T0" -cvf archive.tar.zst directory/Streaming, Ship Data Faster
This is where zstd’s decompression speed shines. Compress on one end, decompress on the other, minimal CPU overhead at the destination:
# Copy a large file over SSH with compressioncat bigfile | zstd | ssh remotehost "zstd -d > /destination/bigfile"
# Database dump over the wirepg_dump mydb | zstd | ssh remotehost "zstd -d | psql targetdb"
# Or with tar, streaming a directorytar -cvf - /data/dir | zstd -T0 | ssh remotehost "zstd -d | tar -xvf - -C /restore/"The fast decompress speed means the receiving end barely notices the CPU cost. You’re essentially getting compression for free on the destination side.
When to Still Use the Old Formats
Look, I’m not here to tell you to rewrite all your scripts tonight. Here’s the decision table:
| Use Case | Use This | Why |
|---|---|---|
| Everyday backups | zstd -3 | Fast, good ratio, parallel |
| Maximum archival compression | xz -6 or zstd -19 | xz compresses faster and usually smaller; zstd decompresses 3.6-6.5x faster |
| HTTP transfer encoding | gzip | Universal browser/server support |
| Log files you rotate | zstd | Fast enough to not add cron lag |
| Distribution tarballs | xz | Universal expectation, tooling |
| Sharing with random external systems | gzip | Every system has gunzip, zero risk |
| Filesystem-level compression (Btrfs/ZFS) | lz4 or zstd (low level) | lz4 when CPU is the limit, zstd for a better ratio |
| Docker images | zstd (opt-in) | Faster layer decompression, but the buildx default is still gzip for compatibility |
Keep using gzip when: compatibility with old or external systems is a hard requirement. HTTP Content-Encoding: gzip is the lingua franca of the web. That’s not changing.
Keep using xz when: you’re making a tarball that’ll go on a distro mirror and you need to hit the smallest possible size. One-time compress, rare decompress, xz’s pain point disappears. Use xz -6, not -9: in our tests -9 gained under 3% and needed 675 MiB of RAM to compress versus 95 MiB.
Stop using bzip2 when: now. Today. Unless something explicitly requires it. There is no scenario where bzip2 beats zstd on both ratio and speed simultaneously. It doesn’t exist.
One Install, Zero Regrets
# Debian/Ubuntusudo apt install zstd
# RHEL/Fedora/Rockysudo dnf install zstd
# Archsudo pacman -S zstd
# Check versionzstd --versionMost modern distros already have it. You might already be done.
The compression landscape finally has a sensible default. zstd didn’t win by being flashy, it won by solving the actual problem: nobody wants to choose between speed and ratio. Facebook’s engineers said “what if you didn’t have to?” and then proved it.
Your 2 AM restore job will thank you.