zstd vs tar.gz: Full Comparison

Share
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, or openssl enc if 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=27 decompression 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.