zstd vs tar.gz: Full Comparison
Use zstd for everything new. It's faster on both ends, compresses better at equivalent CPU cost, and is multi-threaded by default. Stick with gzip/tar.gz only when you need maximum portability to ancient/minimal systems (older than ~2018).
Quick Reference Table
| Dimension | gzip (tar.gz) | zstd (tar.zst) | Winner |
|---|---|---|---|
| Compression speed (default) | ~50–80 MB/s | ~400–500 MB/s | zstd (5–10x) |
| Decompression speed | ~250–400 MB/s | ~1000–1500 MB/s | zstd (3–5x) |
| Compression ratio (default) | ~2.7x | ~2.8x | zstd (slight) |
| Max ratio | gzip -9: ~2.8x | zstd -22: ~3.5x+ | zstd |
| Multi-threading | None (need pigz) |
Native -T0 |
zstd |
| Memory (default) | ~1 MB | ~8 MB | gzip |
| Compatibility | Universal since ~1993 | Linux ≥ 2018, BSD newer | gzip |
| Compression levels | 1–9 | 1–22 (+ --fast, --long) |
zstd |
| Streaming-friendly | Yes | Yes | Tie |
Compression Speed
zstd at default level 3 hits the sweet spot most workloads care about. gzip default is level 6 — slower and worse.
# Realistic numbers on a modern x86 box, 1 GB Linux source tree:
# gzip default (-6, single-threaded): ~25–35s
# pigz default (-6, all cores): ~4–6s
# zstd default (-3, single-threaded): ~3–5s
# zstd -3 -T0 (all cores): ~1–2s
⚠️ Comparing gzip vs zstd single-threaded is the only fair fight. Once you bring -T0 into play, zstd wins by an order of magnitude on most boxes.
Decompression Speed
This is where zstd truly dominates. Decompression speed is essentially flat across all compression levels — you can compress with -19 and still decompress at near-line-rate.
# Same 1 GB dataset:
# gzip: ~3–4s
# zstd: ~0.7–1s
For artifacts you write once and read often (container images, backups you restore frequently, package distribution), this asymmetry matters a lot.
Compression Ratio
# 1 GB Linux source tree, approximate output sizes:
# gzip -1: ~310 MB
# gzip -6: ~250 MB (default)
# gzip -9: ~245 MB
# zstd -1: ~290 MB
# zstd -3: ~245 MB (default — already matches gzip -9)
# zstd -19: ~190 MB
# zstd -22 --long: ~175 MB
# xz -6: ~180 MB (for reference, much slower)
zstd at default already beats gzip's max. For dense binary data the gap narrows; for text/source code it's wider.
Multi-threading
zstd has native multi-threading via -T:
zstd -T0 file # use all cores
zstd -T8 file # use 8 threads
tar --zstd -cf - dir | zstd -T0 -19 -o backup.tar.zst # custom level + threads
gzip is single-threaded. For parallelism you need pigz as a drop-in replacement:
tar -cf - dir | pigz -p 8 > backup.tar.gz
⚠️ pigz is not installed by default on most distros. Standard tar -czf will be single-threaded gzip.
Compression Levels
# zstd
zstd -1 # fastest
zstd -3 # default — already great
zstd -19 # high ratio, still reasonable speed
zstd -22 --ultra --long=27 # max ratio, lots of memory
zstd --fast=10 # even faster than -1
# gzip
gzip -1 # fastest
gzip -6 # default
gzip -9 # marginally better, much slower
The --long=N flag enables a long-range matcher (window of 2^N bytes). Excellent for log files, VM images, backups with repetition spread across long distances. Costs RAM during both compress and decompress — pay attention to --long symmetry.
Memory Usage
# Compression memory footprint (approximate):
# gzip: ~1 MB regardless of level
# zstd -3: ~8 MB
# zstd -19: ~256 MB
# zstd --long=27: ~256 MB during compress AND decompress
⚠️ If you compress with --long=27, the decompressing side also needs that memory. Match the windowLog or fail. Relevant if you're shipping archives to memory-constrained boxes (Raspberry Pi, small VPS).
Compatibility
| Distro / OS | zstd availability |
|---|---|
| Ubuntu | 18.04+ in repos, 20.04+ ships with it |
| Debian | 10+ |
| RHEL / Rocky / Alma | 8+ |
| CentOS 7 | EPEL only |
| CloudLinux 7 | EPEL |
| Alpine | 3.8+ |
| FreeBSD | 12+ base |
| macOS | Homebrew (brew install zstd) |
| Busybox embedded | Often missing |
tar integration:
# zstd support in GNU tar:
tar --zstd -cf archive.tar.zst dir/ # GNU tar 1.31+ (2019)
tar --zstd -xf archive.tar.zst
# Older tar fallback:
tar -cf - dir | zstd -T0 > archive.tar.zst
zstd -dc archive.tar.zst | tar -xf -
⚠️ CentOS 7 ships tar 1.26 — no --zstd flag. You must pipe.
Practical Commands
# 1. Maximum-throughput backup (default level, all cores)
tar -cf - /home | zstd -T0 -3 -o /backup/home.tar.zst
# 2. Maximum-ratio archive (slow, smallest)
tar -cf - /home | zstd -T0 --long=27 -19 -o /backup/home.tar.zst
# 3. Drop-in tar.gz replacement
tar --zstd -cf backup.tar.zst /home # GNU tar 1.31+
# 4. Decompress
tar --zstd -xf backup.tar.zst
zstd -dc backup.tar.zst | tar -xf -
# 5. Verify integrity (zstd has built-in checksums by default)
zstd -t backup.tar.zst
# 6. Streaming over SSH
tar -cf - /var/www | zstd -T0 | ssh backup "cat > /backups/www-$(date +%F).tar.zst"
# 7. Adjust threads if box is busy
zstd -T4 -3 file # leave cores for other work
Real-World Use Cases
| Scenario | Recommendation |
|---|---|
cPanel/CWP /home backups |
zstd -T0 -3 — huge time savings |
Proxmox VM dumps (vzdump) |
Built-in --compress=zstd — use it |
| Log archival (write-once, read-rarely) | zstd -19 --long=27 |
| Docker image layers | zstd already used by containerd/podman |
| Distributing tarball to unknown systems | Stick with tar.gz for compatibility |
| Restic / borg / kopia | They use zstd internally — already optimal |
| MySQL/Mariabackup streaming | mariabackup --stream=xbstream | zstd -T0 |
| Database dumps to S3 | mysqldump … | zstd -T0 -10 |
Hardening Notes
- ✅ zstd has built-in xxh64 checksums enabled by default. gzip's CRC-32 is weaker. Verify with
zstd -t. - ⚠️ Neither provides authentication or encryption. Pipe through
age,gpg, oropenssl encif the archive crosses untrusted networks or storage. - ⚠️ For backups exposed to potential tampering (S3, offsite), use signed archives or AEAD encryption — checksums alone don't defend against intentional modification.
- ⚠️
--long=27decompression memory requirement is a denial-of-service vector if you're decompressing untrusted archives. Cap windowLog on inputs you don't control.
Migration Strategy
Convert existing *.tar.gz backups in-place (streaming, no double disk usage):
gzip -dc old.tar.gz | zstd -T0 -19 -o new.tar.zst && \
zstd -t new.tar.zst && rm old.tar.gz
For Proxmox vzdump, set in /etc/vzdump.conf:
compress: zstd
zstd: 0 # use all cores
For Ansible-managed backup scripts, swap tar -czf for tar --zstd -cf on hosts where GNU tar ≥ 1.31 is available; pipe-fallback elsewhere.
Bottom Line
zstd is a near-strict upgrade. Keep tar.gz only when handing an archive to an unknown third party or a CentOS 6/7 box without EPEL.