pv: The Pipe Viewer You Never Knew You Needed

2026-04-24

You're piping 80 GB of database dump through gzip into mysql and your terminal just... sits there. No output. No progress. Is it 10% done? 95%? Dead? You open htop in another terminal and squint at I/O numbers like a diviner reading tea leaves.

pv (pipe viewer) is a 20-year-old utility that splices into any pipeline and gives you a real-time progress bar, ETA, throughput rate, and total bytes transferred. It's the single most useful tool nobody installs by default.

Install it everywhere:

apt install pv        # Debian/Ubuntu
brew install pv       # macOS
dnf install pv        # Fedora/RHEL

The simplest use — monitor a file copy through a pipe:

pv bigfile.sql.gz | gunzip | mysql mydb
# 3.12GiB 0:02:41 [19.8MiB/s] [============>       ] 62% ETA 0:01:37

That's it. Stick pv in the pipe and suddenly you're not flying blind. But the real power is in the flags most people never discover.

Rate limiting. Need to throttle a restore so your production database doesn't melt?

pv -L 10m backup.sql | mysql prod_db

That's a hard cap at 10 MB/s. No ionice gymnastics, no cgroups. Just -L.

Multiple pv stages. You can insert pv at several points to see where your bottleneck lives:

pv -cN raw dump.sql.gz | gunzip | pv -cN decomp | mysql mydb
#       raw: 1.2GiB 0:01:02 [19.5MiB/s]
#    decomp: 4.8GiB 0:01:02 [78.1MiB/s]

The -c flag enables cursor positioning so multiple bars don't clobber each other, and -N gives each a label. Now you can see the compression ratio in real time and know instantly whether you're I/O bound or CPU bound.

Monitoring data you don't have a file size for. When piping from a stream (no known size), you can supply an estimate:

ssh remote "pg_dump bigdb" | pv -s 50g | gzip > bigdb.sql.gz

The percentage and ETA will be approximate, but approximate beats nothing.

The dd replacement nobody talks about. Writing disk images? Stop using dd with no feedback:

# Instead of: dd if=image.iso of=/dev/sdb bs=4M
pv image.iso | dd of=/dev/sdb bs=4M

Full progress bar. No sending kill -USR1 to a dd process like a caveman.

Generating test data at controlled rates. Need to simulate a log stream at exactly 1000 lines per second?

pv -qL 50k /dev/urandom | base64 | head -c 1G > testdata.bin

The timer trick. Just want elapsed time and average throughput on any pipeline, no progress bar?

some_process | pv -t -b > /dev/null
# 0:00:47  2.31GiB

Flags: -t for timer, -b for total byte count. Clean, scriptable output.

Combining with tar for directory transfers:

tar cf - /data/warehouse | pv -s $(du -sb /data/warehouse | awk '{print $1}') | \
  ssh remote "tar xf - -C /backup"

Accurate progress bar on a remote directory copy, no rsync needed.

The beauty of pv is that it embodies the Unix philosophy perfectly — it does exactly one thing (show you what's moving through a pipe) and composes with everything. Any place you have a |, you can insert pv.

Key Takeaway: Anywhere you have a pipe with no visibility, insert pv — it gives you progress bars, throughput, ETAs, and rate limiting with zero changes to the rest of your pipeline.

All newsletters