watch runs a command over and over, full screen, and shows the latest output. Use it when you’re waiting for something to change: a file growing, a disk filling, connections building up, a deployment rolling out or a queue draining.

watch df -h

By default it re-runs every 2 seconds. Press Ctrl+C to quit.

Options you’ll use

watch -n 5 free -h          # every 5 seconds (decimals work: -n 0.5)
watch -d free -h            # highlight what changed since the last run
watch -d=cumulative free -h # keep highlighting everything that has ever changed
watch -t uptime             # hide the header line
watch -g 'ls /tmp/done'     # exit as soon as the output changes
watch -e 'some-check'       # stop and wait for a key if the command fails
watch -c 'ls --color=always'# interpret colour codes
watch -p -n 1 date          # precise timing (start every second, not 1 s after the last run)

-d is the most useful option. Changes jump out without you having to compare numbers.

Pipes and special characters: quote the command

watch passes its arguments to sh -c. Pipes, redirects and variables need quotes, so they run inside watch instead of in your shell:

watch 'ps aux --sort=-%mem | head -n 10'
watch "ss -tn state established | wc -l"

Without quotes, watch ps aux | head pipes watch’s own output into head, which breaks the display.

Use single quotes when the command contains $ that should be evaluated on every run:

watch 'echo "Load: $(cut -d" " -f1 /proc/loadavg)"'

Aliases from your ~/.bashrc don’t work inside watch, because it uses a fresh sh. Write out the full command.

Practical server examples

# disk filling during an upload, a backup or log growth
watch -d -n 10 'df -h /; du -sh /var/backups'

# memory and swap
watch -n 2 free -h

# a file being written
watch -n 1 'ls -lh /var/backups/db.sql.gz'

# established connections to the web server
watch -d "ss -tn state established '( sport = :443 )' | wc -l"

# top processes by memory
watch -n 3 'ps -eo pid,user,%mem,%cpu,cmd --sort=-%mem | head -n 15'

# Docker containers
watch -n 2 'docker ps --format "table {{.Names}}\t{{.Status}}"'

# Kubernetes pods during a rollout
watch -n 2 kubectl get pods

# MySQL activity
watch -n 2 "mysql -e 'SHOW PROCESSLIST'"

# Redis memory
watch -n 5 "redis-cli INFO memory | grep used_memory_human"

# certificate renewal: expiry date
watch -n 60 'echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate'

# wait for DNS to propagate
watch -n 30 dig +short example.com

Wait for something, then continue

watch -g exits when the output changes, which makes it handy in a one-liner:

watch -g -n 5 'systemctl is-active myapp' && echo "state changed"

In scripts, a plain loop is usually clearer:

until curl -sf http://127.0.0.1:3000/health > /dev/null; do sleep 2; done
echo "app is up"

watch vs tail -f vs top

Want Tool
A command’s output refreshed watch
New lines appended to a log tail -F or journalctl -f
Live processes and load top / htop
Network traffic per interface iftop, nload, vnstat

See head and tail and htop.

Keep watching after you disconnect

watch stops when your SSH session ends. To leave it running on a remote server, start it inside tmux or screen. See tmux.

Quick reference

Task Command
Every 2 s watch cmd
Custom interval watch -n 10 cmd
Highlight changes watch -d cmd
With pipes watch 'cmd | grep x'
Exit on change watch -g cmd
No header watch -t cmd
Colours watch -c cmd

Related: wc and free.

  • Linux
  • Command Line
  • Monitoring