opscape

Archives and compression

zstd

Compress files to .zst with Zstandard, fast at any ratio; decompress with -d, unzstd or zstdcat.

zstd [OPTION...] [FILE...] [-o OUTPUT]

11 of 11 examples run in Debian 13.7 on 2026-10-31 · zstd 1.5.7+dfsg-1 · manual: zstd(1)

Zstandard compresses about as well as gzip at its default level while running several times faster, and decompresses faster than any of the classic tools at every level. Levels go from 1 to 19 (default 3), and --ultra unlocks 20 to 22, where it competes with xz on size. That range makes it the modern default for backups, logs, package archives and data pipelines.

One difference trips up gzip users: zstd keeps the input file unless you pass --rm. Otherwise the conventions are familiar: -d or unzstd decompresses, -c writes to standard output, -o names the output, zstdcat prints, -T0 uses every core.

Directories go through tar: tar --zstd -cf x.tar.zst dir.

Examples

Compress a file

zstd access.log
ls -l access.log*
output
access.log           : 26.72%   (  2.25 KiB =>    617 B, access.log.zst) 
-rw-rw-r-- 1 dev dev 2309 Oct 24 21:55 access.log
-rw-rw-r-- 1 dev dev  617 Oct 24 21:55 access.log.zst

zstd prints a one-line summary (size before and after) and keeps access.log, unlike gzip, bzip2 and xz.

Replace the original with --rm

zstd -q --rm data/sales.csv
ls data/sales*
output
data/sales.csv.zst

--rm deletes the input once the compressed file is written, which is what gzip does by default. -q silences the summary line.

Decompress with -d or unzstd

Setup run before this example
zstd -q --rm todo.txt
unzstd todo.txt.zst
ls todo*
output
todo.txt.zst        : 62 bytes 
todo.txt
todo.txt.zst

unzstd is zstd -d. It reports the decompressed size and, true to form, keeps the .zst file too.

Name the output with -o

zstd -q access.log -o /tmp/requests.zst
ls -l /tmp/requests.zst
output
-rw-rw-r-- 1 dev dev 617 Oct 24 21:55 /tmp/requests.zst

-o FILE writes to an explicit path instead of adding .zst next to the input.

Show sizes and ratio

Setup run before this example
zstd -q access.log
zstd -l access.log.zst
explain
output
Frames  Skips  Compressed  Uncompressed  Ratio  Check  Filename
     1      0     617   B      2.25 KiB  3.742  XXH64  access.log.zst

-l reads the frame headers: compressed and original sizes, and a ratio expressed as original/compressed (3.74 means 3.74 times smaller). XXH64 is the checksum stored with the data.

Pick a level from 1 to 19

zstd -1 -c access.log | wc -c
zstd -19 -c access.log | wc -c
zstd --ultra -22 -c access.log | wc -c
output
623
574
574

Higher levels are smaller and slower to compress; decompression speed barely changes. --ultra is required above 19. On a 2.3 KB log, -19 already finds everything there is to find and -22 adds nothing; the gaps grow with the input.

Read a compressed file without unpacking it

Setup run before this example
zstd -q --rm access.log
zstdcat access.log.zst | grep -c ' 200 '
explain
output
11

zstdcat (same as zstd -dc) streams the decompressed text into the pipeline.

Compress a stream with all cores

cat logs/*.log | zstd -T0 -q > logs.zst
zstd -dc logs.zst | wc -l
output
14

Reading standard input and writing standard output, zstd works like any filter. The glob matched app.log and error.log, 14 lines between them. -T0 uses one thread per core, which pays off on large inputs.

Make and read a .tar.zst

tar --zstd -cf src.tar.zst src
tar -tf src.tar.zst | head -n 3
output
src/
src/lib/
src/lib/helpers.py

GNU tar's --zstd runs the archive through zstd. Listing and extracting need no flag; tar detects the format.

Verify a compressed file

Setup run before this example
zstd -q access.log
head -c 300 access.log.zst > cut.zst
zstd -t access.log.zst
zstd -t cut.zst
outputexit 1
access.log.zst      : 2309 bytes 
cut.zst : Read error (39) : premature end 

-t decompresses in memory and checks the content. The intact file reports its decompressed size; the truncated copy fails with premature end and exit status 1.

Decompressing over an existing file

Setup run before this example
zstd -q notes.txt
zstd -d notes.txt.zst
explain
outputexit 1
zstd: notes.txt already exists; overwrite (y/n) ? Not overwritten  
 

notes.txt is still there (zstd kept it). zstd asks before overwriting; with no answer on standard input, as here, it does not overwrite and exits with 1. -f overwrites without asking.

Common pitfalls

  • zstd keeps its input by default. A script that switches from gzip f to zstd f suddenly doubles disk use; add --rm.

  • Levels above 19 are refused unless you add --ultra, and they need much more memory to compress and decompress.

  • Very old systems may not have zstd (it appeared in distributions around 2018). For files you send to unknown machines, gzip is still the safe choice.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • zstd is one upstream program everywhere; macOS needs it from Homebrew or MacPorts. FreeBSD includes it in the base system.

Options worth knowing

A selection, described in our own words. For the full list, see zstd(1) or zstd --help.

OptionWhat it does
-d, --decompressDecompress.
-z, --compressCompress (the default).
-c, --stdoutWrite to standard output.
-o FILEWrite the result to FILE.
-k, --keepKeep the input file (the default).
--rmDelete the input file after a successful operation.
-f, --forceOverwrite output without asking and accept unusual inputs.
-t, --testVerify the integrity of compressed files.
-l, --listShow information about .zst files.
-T, --threads NCompress with N threads; 0 means one per core. Attach the number: -T0.
-1Fastest standard level (levels 1 to 19; default 3).
-19Strongest standard level.
--ultraAllow levels 20 to 22.
--fast[=N]Use the negative, even faster levels.
--long[=WLOG]Use a long matching window for big inputs with distant repetition.
-q, --quietSuppress the summary line and warnings.
-v, --verbosePrint more detail.
-rOperate recursively on directories.

Related