NetFlow answers the question your monitoring dashboard cannot: not "is the link busy" but "who is making it busy, talking to what, on which port, and how much." It is the difference between knowing you have a problem and knowing what the problem is.
Flexible NetFlow (FNF) is the modern implementation, and it is genuinely flexible: instead of a fixed set of exported fields, you define your own record. That flexibility is also why it confuses people, because you now have three objects to configure instead of one. This article builds the whole thing on Cisco IOS XE, exports the flows to a real Linux collector, and decodes them there, so you can see the data arrive rather than take it on faith. It extends the IP Services cluster guide.
The Three Objects (and Why There Are Three)
Every Flexible NetFlow configuration is built from three pieces that plug into each other. Get the mental model right and the syntax stops being arbitrary.
match / collectThe monitor is the only one that touches an interface. The record and exporter are reusable building blocks: one record can feed many monitors, one exporter can serve many monitors.
match vs collect: The Distinction That Matters
This trips up almost everyone the first time. Inside a flow record:
matchdefines a KEY field. Key fields are the flow's identity. If two packets have identical values across every key field, they belong to the same flow. Change one key field and it is a different flow, with its own cache entry.collectdefines a NON-KEY field. Non-key fields are the things you accumulate or record about the flow: byte counts, packet counts, timestamps, which interface it came in on.
Choosing keys is a real design decision. Match on the classic five-tuple (source IP, destination IP, protocol, source port, destination port) and you get per-conversation visibility. Match only on source and destination IP and you get a much smaller cache but you lose the ability to say "it was all port 443." Match on too much and the cache explodes.
Here is the record from the lab, a standard five-tuple plus ToS:
R1(config)# flow record PINGLABZ-IPV4
R1(config-flow-record)# match ipv4 source address
R1(config-flow-record)# match ipv4 destination address
R1(config-flow-record)# match ipv4 protocol
R1(config-flow-record)# match transport source-port
R1(config-flow-record)# match transport destination-port
R1(config-flow-record)# match ipv4 tos
R1(config-flow-record)# collect counter bytes long
R1(config-flow-record)# collect counter packets long
R1(config-flow-record)# collect timestamp absolute first
R1(config-flow-record)# collect timestamp absolute last
R1(config-flow-record)# collect interface input
R1(config-flow-record)# collect interface outputVerify what the router made of it:
R1#show flow record PINGLABZ-IPV4
flow record PINGLABZ-IPV4:
Description: Key fields identify the flow; nonkey fields are what we count
No. of users: 1
Total field space: 54 bytes
Fields:
match ipv4 tos
match ipv4 protocol
match ipv4 source address
match ipv4 destination address
match transport source-port
match transport destination-port
collect interface input
collect interface output
collect counter bytes long
collect counter packets long
collect timestamp absolute first
collect timestamp absolute lastTotal field space: 54 bytes. That is the per-flow memory cost of your design decision, stated plainly. Add more key fields and this grows, and so does every exported record on the wire.
The Exporter: Getting Flows Off the Box
The cache on the router is useful for a quick look, but the point of NetFlow is shipping the data somewhere it can be stored and queried. In the lab the collector is a real Debian machine at 192.168.99.100 running nfcapd.
R1(config)# flow exporter PINGLABZ-COLLECTOR
R1(config-flow-exporter)# destination 192.168.99.100
R1(config-flow-exporter)# source Ethernet0/0
R1(config-flow-exporter)# transport udp 2055
R1(config-flow-exporter)# export-protocol netflow-v9
R1(config-flow-exporter)# template data timeout 60Two lines deserve attention. source Ethernet0/0 pins the source address of the export packets, which matters because collectors identify exporters by source IP; if it changes, your collector thinks a new device appeared. And template data timeout 60 controls how often the v9 template is resent, which is the single most important setting for a collector that might restart. More on why in the NetFlow versions comparison.
R1#show flow exporter PINGLABZ-COLLECTOR
Flow Exporter PINGLABZ-COLLECTOR:
Description: Export to the Debian VM running nfcapd
Export protocol: NetFlow Version 9
Transport Configuration:
Destination type: IP
Destination IP address: 192.168.99.100
Source IP address: 192.168.99.1
Source Interface: Ethernet0/0
Transport Protocol: UDP
Destination Port: 2055
Source Port: 58769
DSCP: 0x0
TTL: 255
Export template data timeout: 60The Monitor: Cache and Application
R1(config)# flow monitor PINGLABZ-MON
R1(config-flow-monitor)# record PINGLABZ-IPV4
R1(config-flow-monitor)# exporter PINGLABZ-COLLECTOR
!
R1(config)# interface Ethernet0/2
R1(config-if)# ip flow monitor PINGLABZ-MON input
R1(config-if)# ip flow monitor PINGLABZ-MON outputApplying it input and output on the same interface is deliberate. NetFlow is directional: an input monitor sees traffic arriving on that interface only. If you apply it in one direction, you will see half of every conversation and spend an afternoon wondering why the byte counts look wrong.
R1#show flow monitor PINGLABZ-MON
Flow Monitor PINGLABZ-MON:
Flow Record: PINGLABZ-IPV4
Flow Exporter: PINGLABZ-COLLECTOR
Cache:
Type: normal
Status: allocated
Size: 4096 entries / 491584 bytes
Inactive Timeout: 15 secs
Active Timeout: 1800 secsThose two timeouts govern when a flow leaves the cache and gets exported:
A platform note, honestly stated: on the IOL-XE image used for this lab (17.18.2), the cache timeout active and cache timeout inactive commands are rejected under the flow monitor with % Invalid input detected. The defaults above are what you get. On real hardware and on CSR/Catalyst 8000v images those commands work normally, and tuning the active timeout down to 60 seconds is standard practice so long transfers show up in your dashboard while they are still happening. This article shows the default behaviour because that is what the lab genuinely produced.
Reading the Cache
With iperf3 and ping traffic pushed across the monitored interface, the cache fills up:
R1#show flow monitor PINGLABZ-MON cache format table
Cache type: Normal
Cache size: 4096
Current entries: 5
High Watermark: 12
Flows added: 12
Flows aged: 7
- Active timeout ( 1800 secs) 0
- Inactive timeout ( 15 secs) 7
IPV4 SRC ADDR IPV4 DST ADDR TRNS SRC PORT TRNS DST PORT IP PROT intf input intf output bytes long pkts long
=============== =============== ============= ============= ======= ========== =========== =========== =========
192.168.99.100 10.0.10.100 51236 5201 6 Et0/0 Et0/2 1225 14
10.0.10.100 192.168.99.100 5201 51236 6 Et0/2 Et0/0 1017 13
192.168.99.100 10.0.10.100 46438 5201 17 Et0/0 Et0/2 1019948 692
192.168.99.100 10.0.10.100 0 2048 1 Et0/0 Et0/2 252 3
10.0.10.100 192.168.99.100 0 0 1 Et0/2 Et0/0 252 3Every row is a flow, and the key fields are doing exactly what they promised. Protocol 6 is TCP (the iperf3 control connection, both directions, separately). Protocol 17 is UDP, and it is the big one at roughly a megabyte in 692 packets. Protocol 1 is ICMP, and note the port columns: ICMP has no ports, so the router stuffs the type and code in there instead, which is why you see 2048 (type 8, echo request, shifted).
Look at the Flows aged counter: 7 flows aged out via the inactive timeout, 0 via the active timeout. Those 7 are what got exported.
And confirm the export actually happened:
R1#show flow exporter PINGLABZ-COLLECTOR statistics
Flow Exporter PINGLABZ-COLLECTOR:
Packet send statistics (last cleared 00:01:43 ago):
Successfully sent: 2 (594 bytes)
Client send statistics:
Client: Flow Monitor PINGLABZ-MON
Records added: 10
- sent: 7
Bytes added: 540
- sent: 378If Successfully sent stays at 0, the router is not shipping anything and you have a routing or source-interface problem, not a NetFlow problem.
On the Wire
Do not trust the router's own counters alone. Here is tcpdump on the collector, confirming the export packets physically arrive:
j@llmbits:~$ sudo tcpdump -i ens224 -n udp port 2055
listening on ens224, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:44:51.294654 IP 192.168.99.1.58769 > 192.168.99.100.2055: UDP, length 296
18:44:52.295245 IP 192.168.99.1.58769 > 192.168.99.100.2055: UDP, length 132Two packets, two different sizes, and that difference is the whole of NetFlow v9 in one screen. The 296-byte packet is the template: it tells the collector what the fields are and in what order. The 132-byte packet is the data: just values, no field names, because the collector already knows the layout from the template.
This is why template data timeout matters so much. If your collector restarts and misses the template, it cannot decode a single data packet until the next template arrives. Sixty seconds of blindness is tolerable. The default of 600 is not.
Decoded on the Collector
The final proof. nfcapd wrote the flows to disk; nfdump reads them back:
j@llmbits:~$ nfdump -R /home/j/nfcap -o long
Date first seen Duration Proto Src IP Addr:Port Dst IP Addr:Port Packets Bytes Flows
2026-07-11 17:45:30.137 00:00:02.004 ICMP 192.168.99.100:0 -> 10.0.10.100:8.0 3 252 1
2026-07-11 17:45:30.139 00:00:02.004 ICMP 10.0.10.100:0 -> 192.168.99.100:0.0 3 252 1
2026-07-11 17:46:38.021 00:00:05.073 TCP 192.168.99.100:40556 -> 10.0.10.100:5201 16 1488 1
2026-07-11 17:46:38.023 00:00:05.070 TCP 10.0.10.100:5201 -> 192.168.99.100:40556 14 1222 1
2026-07-11 17:46:38.031 00:00:05.058 TCP 192.168.99.100:40570 -> 10.0.10.100:5201 6467 9.7 M 1
2026-07-11 17:46:38.033 00:00:05.060 TCP 10.0.10.100:5201 -> 192.168.99.100:40570 3653 194636 1
2026-07-11 17:46:38.037 00:00:05.047 TCP 192.168.99.100:40578 -> 10.0.10.100:5201 5425 8.1 M 1
2026-07-11 17:46:43.111 00:00:05.002 UDP 192.168.99.100:51231 -> 10.0.10.100:5201 1728 2.5 M 1
2026-07-11 17:46:48.132 00:00:01.001 ICMP 192.168.99.100:0 -> 10.0.10.100:8.0 2 168 1
Summary: total flows: 14, total bytes: 20.7 M, total packets: 20412, avg bps: 2.1 M, avg pps: 258, avg bpp: 1016
Time window: 2026-07-11 17:45:30 - 2026-07-11 17:49:00, Duration: 00:03:30.000Fourteen flows, 20.7 megabytes, 20,412 packets, and every one of them is a real packet the router forwarded and reported. The two parallel TCP streams at 9.7M and 8.1M are the iperf3 -P 2 test. The 2.5M UDP flow is the UDP test. The asymmetry between the TCP send and receive rows (9.7 M out, 194 KB back) is exactly what a bulk transfer looks like: data one way, ACKs the other.
That is the payoff. Not a config snippet, but the actual data landing somewhere you can query it.
Sampling, and Not Melting the Box
NetFlow costs CPU and memory. On a busy edge router, processing every single packet is not free, and a 4096-entry cache fills fast on a real internet link.
Two mitigations:
- Sampling. A
flow samplertells the router to process 1 in N packets. Statistically valid for volume trends, useless for security forensics (you will miss the one packet that mattered). Sample on high-speed core links, never on a link you are using for incident response. - Cache sizing.
cache entries Nraises the ceiling. Watch theHigh Watermarkin the cache output. If it is pinned at your cache size, you are dropping flows and your data is silently incomplete.
The general rule: monitor the interfaces where the answer lives (edge, WAN, DMZ), not every interface on every box. NetFlow on a core link between two aggregation switches usually tells you nothing you cannot get from an interface counter.
When It Does Not Work
show flow interface.Successfully sent: 0. Check routing to the collector.template data timeout.input only. Add output.And when you need to see individual packets rather than flow summaries, that is a different tool: see SPAN, RSPAN, and ERSPAN for packet mirroring, and conditional debugging for surgical debug output.
FAQ
Do I need a flow exporter?
No. A monitor with only a record is valid and gives you show flow monitor cache on the box. That is genuinely useful for live troubleshooting. You need an exporter when you want history.
What port should the collector listen on?
2055 is the convention, but nothing enforces it. 9995 and 9996 are also common. Match the router config to whatever your collector expects.
Can I run NetFlow and a packet capture at the same time?
Yes. They answer different questions. NetFlow tells you which conversations exist and how big they are; a capture tells you what was inside them.
Does NetFlow see traffic the router drops with an ACL?
Input NetFlow is processed before the outbound ACL, so denied traffic can still appear in the cache. That is useful (you can see what is being blocked) and occasionally confusing (the flow shows bytes that never reached the destination).
Key Takeaways
- Three objects: record (what), exporter (where), monitor (glue plus cache). Only the monitor is applied to an interface.
match= key field = flow identity.collect= non-key field = what you count. The record's "total field space" is the memory cost of your choices.- Apply the monitor
inputANDoutput, or you will only see half of every conversation. - NetFlow v9 sends a template packet and data packets separately. If the collector misses the template it decodes nothing, so keep
template data timeoutlow. - Verify at three layers:
show flow monitor cache(the router saw it),show flow exporter statistics(the router sent it), and the collector itself (it arrived and decoded). - On this IOL-XE image the cache timeouts cannot be tuned and sit at the 1800s/15s defaults. On production hardware, tune them.
Next: NetFlow v5 vs v9 vs FNF vs IPFIX, or the IP Services cluster guide.