Back to blog
·10 min read·productdevbook

How nettop Works on macOS (And Why You Want a UI for It)

A friendly tour of nettop on macOS: what it shows, what it does not, and where a graphical bandwidth monitor takes over.

  • Developer tools
  • macOS
  • Network monitoring
  • Tutorial

Open Terminal and type nettop. Within a second you have a live, top-style display of every process on your Mac doing network I/O — bytes in, bytes out, the routes they're taking, the protocols they're using. It's a tool you didn't know you had, and once you find it you wonder why you ever opened Activity Monitor's Network tab. This is a tour of nettop macos, the flags that actually matter, and the places nettop runs out of road.

Spoiler: nettop is great for a one-shot look. It's not great for "what was Slack doing yesterday at 3 PM?" That's the gap a UI on top of these same kernel counters fills.

What nettop macos actually is

nettop ships with macOS, lives at /usr/bin/nettop, and has been there since at least 10.7. It's a small command-line frontend over the same kernel APIs Activity Monitor and Instruments use — primarily proc_pidinfo with the socket and stats variants, plus per-route counters. It does not require root for most uses. It does not capture packets. It does not need an installer.

Run it with no flags:

nettop

You'll see something like:

Processes
  Google Chrome.40621      tcp4         34.117.x.x.443        Established
  Google Chrome.40621      tcp4         142.250.x.x.443       Established
  Slack Helper.41218       tcp4         52.5.x.x.443          Established
  ...

Press q to quit, ? for help, r to toggle rates vs totals, d to toggle delta mode.

The flags worth knowing

There are a lot of nettop flags. Most are noise. These are the ones I actually use.

-m for mode

-m route shows per-route traffic — useful to see whether you're going over the local network, the VPN tunnel, or the default route.

-m tcp and -m udp filter to one protocol.

-m process is the default and the one you want most of the time.

-P for process picker

-P makes nettop run once and dump a static snapshot, instead of refreshing live. This is what you want when scripting it or piping to another tool.

nettop -P -L 1

That runs nettop in non-interactive mode for one sample and exits. Combine with -J to limit columns (more on that below) for clean log output.

-L for log mode

-L <count> runs nettop in log mode for a given number of samples and exits. -L 0 runs forever, dumping a sample line every interval. The default interval is 1 second; change it with -s.

nettop -L 60 -s 1 -P -J bytes_in,bytes_out,interface,state

That captures one sample per second for 60 seconds, with just the columns you care about, and exits. Pipe to a file for later analysis.

-J for column selection

-J takes a comma-separated list of column names. Useful columns:

  • bytes_in, bytes_out — totals since the process started
  • rx_dupe, rx_ooo — TCP retransmits and out-of-order packets
  • re-tx — retransmits
  • rtt_avg — round-trip time, useful for spotting slow networks
  • rcvsize, tx_win — socket buffer state
  • interface — which interface (en0, utun0, lo0)
  • state — connection state (Established, TimeWait, Listen)

The full list is in man nettop. There are about 30 columns; most of the time bytes_in,bytes_out,interface,state is plenty.

-x for XML

-x outputs in plist XML format — parseable, painful to read, but useful if you want to feed nettop into another script. In practice the JSON-shaped column output via -J and -L is usually easier to work with.

-k for hide

-k <column> hides a column you don't care about. The opposite of -J. Use it interactively when you want to keep the live display but trim the noise.

Reading the output

A few things to know about the numbers nettop macos reports:

Totals are since the process started, not since you launched nettop. A long-lived process like cloudd may show hundreds of MB even on a quiet system. Use delta mode (d interactively, or compute deltas from -L log output) to see actual rates.

Each row is a flow, not a process. A single Chrome process with 40 tabs may have 40+ rows. Aggregate by PID yourself — nettop won't.

Helper processes show up by their helper name. Google Chrome Helper (Renderer), Slack Helper, com.docker.backend. There's no folding. You see the raw kernel view.

Routes can be surprising. A connection going through a VPN tunnel will show interface utun0 or similar, not en0. If you see traffic on an interface you don't recognize, that's worth investigating.

Where nettop falls down

For a free, built-in tool, nettop is genuinely useful. But there are real limits.

No history

Quit nettop and the data is gone. There's no log file, no rolling window, no persisted state. If you weren't watching when the spike happened, you didn't see it. -L log mode helps if you set it up before the event you want to capture, but that's planning, not debugging.

No helper folding

As noted, you see Google Chrome Helper (GPU), (Plugin), (Renderer), etc., as separate rows. Mentally summing them across 30 short-lived renderer processes is tedious and error-prone.

Terminal-only

A live nettop session takes a Terminal window. If you want to glance at "is something using bandwidth right now?" while writing code, you have to switch contexts. There's no menu bar widget, no notification, no tray icon.

Confusing column names

re-tx, rtt_var, tx_dupe — the columns are named for kernel TCP statistics, not for humans. The man page is your friend, but the learning curve is real.

No filters by app or category

You can't say "show me only Chrome and its helpers" or "show me everything except system processes." You filter by piping to grep, which works but isn't pleasant.

No alerting

If you want to know when an app suddenly starts using 50 MB/s, nettop won't tell you. You'd need to wrap it in a polling script that diffs samples and compares against a threshold.

See ova in action

A glance-able menu bar bandwidth monitor — local, signed, ~3 MB.

Download for macOS

Why a UI helps

Most of the time, you don't actually want to know per-flow TCP statistics. You want to know:

  1. Is something using bandwidth right now?
  2. Which app is it?
  3. How much has each app used in the last hour, day, week?

A menu bar app like ova reads roughly the same kernel data nettop reads, but does three things on top:

  • Folds helper processes under their parent app, so "Slack" is one row
  • Stores history locally, so you can scrub back and ask "what happened at 2:47 PM"
  • Lives in the menu bar, so it's a glance away while you're working

That's not better than nettop — it's different. nettop is a debugging tool you reach for in moments. A bandwidth monitor is ambient awareness.

Live + history in one place
ova shows the current rate in your menu bar and a full scrubable timeline behind it. nettop shows the live rate. ova also keeps yesterday at 3 PM available.

Combining nettop with other tools

nettop pairs well with a few other built-ins.

nettop + lsof

nettop shows you which PID is using bandwidth. lsof -p <PID> shows you every file and socket that PID has open. Together they answer "which remote endpoint is this app talking to?"

lsof -i -P -n -p 41218

That lists every IPv4/IPv6 socket open by PID 41218, with remote addresses and ports.

nettop + log show

If you suspect a system service is misbehaving, the unified log usually has more context than nettop. After spotting an offending process in nettop, run:

log show --last 5m --predicate 'process == "cloudd"'

…to see what cloudd was doing at the same time.

nettop + tcpdump

When you need to know what's actually on the wire, packet capture is the answer. nettop tells you "Chrome is uploading 12 MB/s to 142.250.x.x". sudo tcpdump -i en0 host 142.250.x.x tells you whether it's HTTP/2 or QUIC, what the SNI was, what response codes are coming back.

A worked example

Suppose your laptop's fan kicks up while you're idle. Chain of investigation:

  1. Open a terminal, run nettop -P -L 5 -s 2 -J bytes_in,bytes_out,interface.
  2. Read the output. Suppose cloudd shows 80 MB up over 10 seconds.
  3. Run log show --last 1m --predicate 'process == "cloudd"' --info.
  4. The log says iCloud is uploading photos taken on your phone earlier today.
  5. Decide whether to wait it out, throttle iCloud, or pause the upload.

That whole sequence takes about 90 seconds. If you'd installed ova the day before, step 1 would be "look at the menu bar," and you'd already know cloudd was the culprit before opening a terminal. The unified log step still applies.

Wrapping up

nettop macos is a small, fast, built-in tool that nobody markets. Once you know it exists, it's the right answer for "I need to see what's using my network right now." Its limits are real — no history, no folding, terminal-only — but inside its scope it's hard to beat for free.

For ambient awareness, scrubable history, and per-app folding without typing a command, you want a menu bar app. ova does that part: about 3 MB, macOS 14+, Apple Silicon and Intel, samples at roughly 1 Hz, stores everything locally on disk. nettop and ova solve adjacent problems, and most macOS users benefit from having both.