mbuffer: The Memory Buffer That Saves Your Tape Drives, ZFS Sends, and Slow Pipes

2026-05-23

You know pv. You've used it for years. But pv is a meter — it shows you throughput while passively copying bytes. When your pipe has a slow reader and a bursty writer (or vice versa), pv just watches the carnage. What you want is a real buffer: a chunk of RAM that absorbs the spikes so neither side stalls. That's mbuffer, written by Thomas Maier-Komor around 2001, still actively maintained, and quietly powering serious infrastructure.

The original use case was tape drives. Modern LTO writes at 300+ MB/s but only if you feed it continuously — pause for a second and the drive does a "shoe-shine" backhitch that destroys throughput and wears the tape. Same problem appears with ZFS send/recv, network sockets, and anything where pipeline jitter hurts.

Basic usage: stick a 1 GB buffer in any pipeline.

tar cf - /data | mbuffer -m 1G | ssh backup-host 'cat > backup.tar'

You get a live TUI showing in-rate, out-rate, buffer fill percentage, and total bytes. When you see the buffer pegged at 100%, the reader is the bottleneck. When it sits at 0%, the writer is. That alone beats hours of guessing.

Where it really shines. mbuffer speaks TCP natively, replacing the socat/nc dance entirely:

# Receiver (waits for data on port 9090, 2 GB buffer)
mbuffer -I 9090 -m 2G -o /tank/restore.img

# Sender (streams a ZFS snapshot to the receiver)
zfs send tank/db@nightly | mbuffer -m 2G -O receiver.lan:9090

This is the canonical ZFS-replication recipe for a reason: ssh adds CPU overhead and tiny window sizes, while raw mbuffer-to-mbuffer uses tuned TCP buffers and dual rings of RAM. Add -s 1M to set block size to match your pool's recordsize and you'll saturate 10 GbE without breaking a sweat.

Tee to multiple destinations — without losing throughput to the slowest one:

dd if=/dev/nvme0n1 bs=1M | mbuffer -m 4G \
  -o /mnt/backup1/disk.img \
  -o /mnt/backup2/disk.img \
  -o >(sha256sum > disk.sha256)

Each output gets its own ring; a stalled drive doesn't block the others until the buffer fills.

Rate limiting for when you don't want to saturate the link during business hours:

pg_dump huge_db | mbuffer -m 512M -r 50M | ssh archive 'cat > db.sql'

That -r 50M caps output at 50 MB/s. pv -L does this too, but pv's buffer is a single pipe's worth (~64 KB on Linux); mbuffer's buffer is whatever you tell it.

The hashing trick. Compute checksums while streaming, no extra pass:

cat huge.iso | mbuffer -m 1G -H sha256 -o /dev/st0

You write to tape and get a SHA-256 of the actual stream, useful for verifying restores years later.

Why not just pv? pv is wonderful for visibility, but its buffer is whatever the kernel pipe gives you. mbuffer lets you say "give me 8 GB of RAM, split into 4 MB blocks, and don't even start writing until you have 20% filled" (-P 20). That start-threshold is what stops tape backhitches and slow-start TCP stalls. It's the difference between a meter and an actual shock absorber.

Install it: apt install mbuffer, brew install mbuffer, in every BSD ports tree. Read man mbuffer — every flag is there for a real-world horror story someone lived through.

Key Takeaway: When a pipeline's two ends run at uneven speeds, pv tells you about the pain — mbuffer absorbs it with a real RAM buffer, optional network transport, multi-output tee, and rate limiting that's been keeping tape drives and ZFS sends happy since 2001.

All newsletters