2026-09-09
Every Unix greybeard has hit this wall: someone hands you a 40 GB backup.tar.xz, you need one config file out of it, and plain tar -xJf spends 12 minutes decompressing the whole stream just to reach the file 3% of the way in. xz is single-threaded by default and, more importantly, has no seek table — the decoder has to walk the stream from byte zero. pigz and pbzip2 parallelize compression but still produce sequential-access files. pixz (by Jonathan Nieder / Dave Vasilevsky, packaged as pixz on Debian, Ubuntu, Fedora, Arch, Homebrew) fixes both problems in one 2010-vintage binary.
Two features nobody else combines:
# Compress a tar stream in parallel, with index
tar -cf - big-directory/ | pixz > big-directory.tpxz
# Or use it as tar's compressor directly
tar -Ipixz -cf big-directory.tpxz big-directory/
# List contents without decompressing the payload
pixz -l big-directory.tpxz
# Extract ONE file — reads only the relevant xz block
pixz -x big-directory/etc/nginx.conf < big-directory.tpxz | tar -xf -
The .tpxz extension is convention, not requirement — the file is still valid xz. Any xz decoder reads it linearly; only pixz uses the index. That backward compatibility is why it survives on production backup pipelines from 2012.
$ ls -lh linux-6.11.tar.xz linux-6.11.tpxz
-rw-r--r-- 1.4G linux-6.11.tar.xz
-rw-r--r-- 1.5G linux-6.11.tpxz # ~7% larger, block boundaries cost entropy
$ time tar -xJf linux-6.11.tar.xz linux-6.11/MAINTAINERS
real 2m18.4s # decodes the entire prefix
$ time pixz -x linux-6.11/MAINTAINERS < linux-6.11.tpxz | tar -xf -
real 0m0.31s # index → one block → done
Four hundred times faster for the one-file case. The compression side is roughly Ncores× on a modern box; a 16-core desktop compresses at ~200 MB/s where single-threaded xz plods along at 15.
Point pixz -t at any file for pure parallel xz without the tar index (useful for logs, VM images, database dumps):
pg_dump mydb | pixz -t > mydb.sql.xz
pixz -d < mydb.sql.xz | psql restored
# Compression level (0-9, default 6). -9 is legendarily slow but small.
pixz -9 < big.tar > big.tpxz
# Cap threads (defaults to all cores — bad on shared machines)
pixz -p 4 < big.tar > big.tpxz
# Different block size — larger blocks compress better, hurt seek granularity
pixz -b $((16*1024*1024)) < big.tar > big.tpxz
The block size is the real dial: 4 MB gives per-file seeks in most tars, 64 MB gives xz-competitive ratios but coarser seeks. The default (guessed from input size) is usually right.
xz -T0?Modern xz gained multi-threaded compression years ago — good. But it still writes a stream that requires linear decompression, and it has no tar-member index. xz -T0 replaces half of pixz. If your workflow ever includes "extract one file from a large tarball," pixz is still the only tool that answers "which byte range do I need to decompress?"
.tar.xz from a write-once/read-all archive into a seekable one, giving you parallel compression and single-file extraction in milliseconds instead of minutes — for the price of a ~7% size bump and one apt-get.
