There is a particular kind of fear that stops engineers from using the most powerful troubleshooting tool on a Cisco router. You type debug ip packet, the console erupts, the CPU pins at 100 percent, the box stops forwarding, and someone asks why the site went down. After that happens once, "never debug on a production router" becomes received wisdom.
It is the wrong lesson. The problem was never debugging, it was unfiltered debugging. Conditional debug lets you ask the router a narrow question and get a narrow answer. This article proves that with real output from a lab: the same debug, on the same router, in two identical time windows, produces ten lines or five depending on one condition. It extends the IP Services cluster guide.
Why Debug Melts the Box
Understand the mechanism and the fix becomes obvious.
Normal forwarding happens in CEF, in the fast path, and never troubles the CPU. When you enable debug ip packet, the router must inspect and report on packets, which means punting them to the CPU for process switching. Two things then compound:
- Every packet now costs CPU cycles instead of being switched in hardware or optimized code.
- Every packet generates a log message, which itself costs CPU, and if console logging is on, that message is written to a 9600-baud console port. The console becomes the bottleneck, and the router blocks on it.
On a router forwarding 50,000 packets per second, that is 50,000 punts and 50,000 log messages per second. The CPU has no chance. This is a self-inflicted denial of service, and the router will not stop to warn you.
The fix is not to avoid debug. The fix is to make the router only punt and report on the handful of packets you actually care about.
First: The Safety Net
Before you enable any debug on a box that matters, three lines:
R1(config)# no logging console
R1(config)# logging buffered 256000 debugging
R1(config)# service timestamps debug datetime msecno logging console is the single most important line here. It sends debug output to a memory buffer instead of the console port, which removes the 9600-baud bottleneck entirely. You then read the buffer at your leisure with show logging. Many production outages blamed on "debug" were really caused by console logging.
service timestamps debug datetime msec gives millisecond timestamps, which you will want the moment you are trying to work out the order of events. Every capture below depends on them.
And know your escape hatch before you need it: undebug all, or u all, which you can type in under a second while panicking.
The Lab Behind Every Capture Here
Everything below came off one IOS XE 17.18.02 router in CML. R1 has two OSPF neighbors on two separate interfaces, which is what makes the filtering visible:
R1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 10.50.13.1 YES TFTP up up
Ethernet0/1 10.50.100.2 YES TFTP up up
Loopback0 10.255.1.1 YES TFTP up up
R1#show ip ospf neighbor
Neighbor ID Pri State Dead Time Address Interface
2.2.2.2 1 FULL/DR 00:00:37 10.50.100.3 Ethernet0/1
3.3.3.3 1 FULL/DR 00:00:37 10.50.13.3 Ethernet0/0R3 sits behind Ethernet0/0. R2 and a Linux host sit on the Ethernet0/1 LAN. Two adjacencies, two interfaces, one router: a small enough stage that you can count the debug lines by hand.
debug condition: The Same Debug, Two Different Answers
For protocol debugs (OSPF, BGP, DHCP, and so on), the tool is debug condition. It sets a global filter that any condition-aware debug will respect. Here is what that is worth, measured.
Without a condition
debug ip ospf hello, left running for 25 seconds, no condition set:
R1#debug ip ospf hello
OSPF hello debugging is on
R1#show logging | begin Log Buffer
Log Buffer (256000 bytes):
*Aug 29 19:55:37.227: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3
*Aug 29 19:55:37.789: OSPF-1 HELLO Et0/1: Rcv hello from 2.2.2.2 area 0 10.50.100.3
*Aug 29 19:55:40.658: OSPF-1 HELLO Et0/0: Send hello to 224.0.0.5 area 0 from 10.50.13.1
*Aug 29 19:55:42.151: OSPF-1 HELLO Et0/1: Send hello to 224.0.0.5 area 0 from 10.50.100.2
*Aug 29 19:55:46.797: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3
*Aug 29 19:55:47.691: OSPF-1 HELLO Et0/1: Rcv hello from 2.2.2.2 area 0 10.50.100.3
*Aug 29 19:55:50.568: OSPF-1 HELLO Et0/0: Send hello to 224.0.0.5 area 0 from 10.50.13.1
*Aug 29 19:55:52.143: OSPF-1 HELLO Et0/1: Send hello to 224.0.0.5 area 0 from 10.50.100.2
*Aug 29 19:55:55.840: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3
*Aug 29 19:55:56.895: OSPF-1 HELLO Et0/1: Rcv hello from 2.2.2.2 area 0 10.50.100.3
*Aug 29 19:56:00.035: OSPF-1 HELLO Et0/0: Send hello to 224.0.0.5 area 0 from 10.50.13.1Ten hello lines, both interfaces, sends and receives interleaved. On a lab router with two neighbors that is readable. On a distribution router with forty OSPF adjacencies it is 200 lines per hello interval and you will never find the one you came for.
With an interface condition
Same debug, same router, same 25 second window, one extra command first:
R1#debug condition interface Ethernet0/0
Condition 1 set
R1#show debug condition
Condition 1: interface Et0/0 (1 flags triggered)
Flags: Et0/0
R1#debug ip ospf hello
OSPF hello debugging is on
R1#show logging | begin Log Buffer
Log Buffer (256000 bytes):
*Aug 29 19:56:33.228: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3
*Aug 29 19:56:38.182: OSPF-1 HELLO Et0/0: Send hello to 224.0.0.5 area 0 from 10.50.13.1
*Aug 29 19:56:42.889: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3
*Aug 29 19:56:47.738: OSPF-1 HELLO Et0/0: Send hello to 224.0.0.5 area 0 from 10.50.13.1
*Aug 29 19:56:52.545: OSPF-1 HELLO Et0/0: Rcv hello from 3.3.3.3 area 0 10.50.13.3Five lines, every one of them Et0/0. Not a single line about Ethernet0/1, even though that adjacency was up and exchanging hellos on exactly the same schedule. The condition did not filter the display, it stopped those events from ever becoming debug messages.
Turning it off tells you the condition was really there:
R1#undebug all
1 conditions have been removed
All possible debugging has been turned offThe Trap: A Condition the Debug Does Not Understand
This is the part that catches people, and it is why "conditional debug" deserves more than a bullet point. Condition support is per debug and per condition type. A debug that honors debug condition interface may know nothing about debug condition ip, and when a debug does not understand the condition in force, you do not get a warning. You get silence.
Same router, same OSPF hello debug, same 25 second window. The only change is that the condition is now an IP address instead of an interface:
R1#debug condition ip 10.50.13.3
Condition 1 set
R1#show debug condition
Condition 1: ip 10.50.13.3 :: (0 flags triggered)
R1#debug ip ospf hello
OSPF hello debugging is on
R1#show logging | begin Log Buffer
Log Buffer (256000 bytes):
*Aug 29 20:02:15.364: %SYS-4-LOG_CLEARED: Logging buffer was cleared by user UnknownZero debug lines. The adjacency never dropped. Hellos kept flowing from 10.50.13.3, the exact address in the condition. The debug simply does not evaluate IP conditions, so with one in force it reported nothing at all.
Now compare the two show debug condition outputs, because the router did tell you, quietly:
interface Et0/0 (1 flags triggered)A running debug is actually evaluating this condition. Output will be scoped.ip 10.50.13.3 :: (0 flags triggered)Nothing is evaluating it. Expect silence, not scoping. Change the condition type or drop it.The flags-triggered count is the diagnostic. Zero flags with debugs enabled means the condition and the debug are not talking to each other, and you are about to conclude a protocol is dead when it is merely being filtered into nothing. Check show debug condition before you check anything else. A stale condition left over from last week is one of the better ways to waste an afternoon.
IOS is aware this is dangerous in the other direction too. Remove the last interface condition while debugs are running and it stops to ask:
R1#no debug condition interface Ethernet0/0
This condition is the last interface condition set.
Removing all conditions may cause a flood of debugging
messages to result, unless specific debugging flags
are first removed.
Proceed with removal? [yes/no]: y
Condition 1 has been removedRead that prompt rather than reflexively answering yes. It is telling you that the firehose you carefully avoided is one keystroke away.
What Conditions Your Box Actually Offers
Do not trust a list from an article, including this one. Ask the router, because the set varies by platform and release:
R1#debug condition ?
called called number
calling calling
cpl Cisco Provisioning Language debugging
cts Cisco Trusted Security
domain domain name
glbp interface group
interface interface
ip IP address
mac-address MAC address
mst mst
portbundle Port bundle host key to uniquely identify session/service
session-id session number
standby interface group
tnlmsg tnlmsg
username username
vcid VC ID
vrf Virtual Routing and Forwarding
xconnect Xconnect conditional debugging on segment pairThe ones worth committing to memory are interface, ip, vrf (invaluable on an MPLS L3VPN PE carrying hundreds of customers), username, mac-address and standby.
Two honest absences on this image, both worth knowing before you go looking for them. There is no match-list keyword, so the class-map style conditional debug documented for some platforms is not available here. And debug platform condition, the hardware-path conditional debug you would use on a Catalyst 9000 or an ASR, does not exist either:
R1#debug platform ?
hardware Debug platform hardware information
software Debug platform software informationThat is the general rule with conditional debugging. The feature is real and it is worth learning, but coverage is uneven, so verify on the box in front of you rather than on the box in the documentation.
Filtering debug ip packet With an ACL
debug ip packet does not use debug condition. It takes an access list directly, and only packets permitted by that ACL are debugged. This is the workhorse and the one most people never learn.
Here is the test. Three separate sources sent traffic to R1 in the same window: R3 from 10.50.13.3, and R2 twice, from 10.50.23.2 and from 10.50.100.3. The ACL permits one of them.
R1(config)#access-list 99 permit 10.50.13.3
R1#debug ip packet 99
IP packet debugging is on for access list 99R1#show logging | begin Log Buffer
*Aug 29 19:59:46.365: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, rcvd 0
*Aug 29 19:59:46.365: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, input feature, packet consumed, MCI Check(110), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Aug 29 19:59:48.669: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, input feature, MCI Check(110), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Aug 29 19:59:48.669: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, rcvd 2
*Aug 29 19:59:48.670: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, stop process pak for forus packet
*Aug 29 19:59:48.670: IP: tableid=0, s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (Loopback0) nexthop=10.255.1.1, routed via RIB
*Aug 29 19:59:55.656: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, rcvd 0
*Aug 29 19:59:55.656: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, input feature, packet consumed, MCI Check(110), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Aug 29 20:00:05.389: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, rcvd 0
*Aug 29 20:00:14.893: IP: s=10.50.13.3 (Ethernet0/0), d=224.0.0.5 (nil), len 100, rcvd 0Every line reads s=10.50.13.3. Nothing from R2's two pings, which crossed the same router in the same seconds. The ACL kept them out of the CPU entirely.
But look closer and the capture teaches a second lesson. Half those lines are destined to 224.0.0.5, which is OSPF, not the ping you were chasing. A standard ACL matches on source address only, so it caught R3's control plane along with R3's data. On a real router where the source is a busy host, that difference is the difference between fifty lines and fifty thousand.
Tighten it with an extended ACL
Same test, same traffic, one better ACL:
R1(config)#access-list 199 permit icmp host 10.50.13.3 host 10.255.1.1
R1#debug ip packet 199
IP packet debugging is on for access list 199
R1#show logging | begin Log Buffer
*Aug 29 20:01:47.147: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, input feature, MCI Check(110), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Aug 29 20:01:47.147: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, rcvd 2
*Aug 29 20:01:47.147: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, stop process pak for forus packet
*Aug 29 20:01:47.147: IP: tableid=0, s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (Loopback0) nexthop=10.255.1.1, routed via RIB
*Aug 29 20:01:47.149: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, input feature, MCI Check(110), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Aug 29 20:01:47.149: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, rcvd 2
*Aug 29 20:01:47.149: IP: s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (nil), len 100, stop process pak for forus packet
*Aug 29 20:01:47.149: IP: tableid=0, s=10.50.13.3 (Ethernet0/0), d=10.255.1.1 (Loopback0) nexthop=10.255.1.1, routed via RIBEight lines, two echoes, no OSPF. Protocol and destination in the ACL are what got you there. Write the extended ACL first, not after the console has already filled.
One syntax trap: on this image debug ip packet accepts a numbered access list only. Point it at a named ACL and it rejects the name outright.
R1#debug ip packet DBG-R3-ICMP
^
% Invalid input detected at '^' marker.Reading the output
The line itself is dense but rewards attention:
s= / d=Source and destination, with the interface the packet arrived on in parentheses.rcvd 2Receive code. 2 means "for us, and we are going to process it." rcvd 0 on the 224.0.0.5 lines is a multicast the router consumed.routed via RIBThe forwarding decision, and which table made it. tableid=0 is the global table, a VRF shows a different id.input feature, ...The feature chain the packet traverses, named in the order it runs.That feature chain is quietly one of the most useful things in the output. When a packet is being dropped or mangled, the chain tells you which feature to blame and where in the packet's journey it sits. If you have a Flexible NetFlow monitor on the interface, you will see it named there.
What Debug Cannot See: The CEF Blind Spot
You will sometimes enable debug ip packet with a perfectly good ACL, send traffic you know is crossing the router, and see nothing at all. This is not a broken debug. It is CEF, and it is worth proving rather than asserting.
The setup: a transit flow from R3 (10.50.13.3) through R1 to a Linux host on the far LAN (10.50.100.50). R1 forwards it, so it is CEF switched. An ACL was written to match that flow exactly, and an Embedded Packet Capture point was started on the same ingress interface at the same time.
R1(config)#access-list 150 permit ip host 10.50.13.3 host 10.50.100.50
R1#debug ip packet 150
IP packet debugging is on for access list 150
R1#monitor capture buffer PLBUF size 256 max-size 1518 linear
R1#monitor capture point ip cef PLPOINT Ethernet0/0 both
R1#monitor capture point associate PLPOINT PLBUF
R1#monitor capture point start PLPOINTR3#ping 10.50.100.50 source Ethernet0/0 repeat 4
Sending 4, 100-byte ICMP Echos to 10.50.100.50, timeout is 2 seconds:
Packet sent with a source address of 10.50.13.3
!!!!
Success rate is 100 percent (4/4), round-trip min/avg/max = 3/3/5 msFour pings, all successful, all through R1. Here is what the debug logged:
R1#show logging | begin Log Buffer
Log Buffer (256000 bytes):
*Aug 29 20:08:39.507: %SYS-4-LOG_CLEARED: Logging buffer was cleared by user UnknownNothing. Now the capture point, watching the identical interface over the identical seconds:
R1#show monitor capture buffer PLBUF parameters | include Packets
Buffer Size : 262144 bytes, Max Element Size : 1518 bytes, Packets : 13
R1#show monitor capture buffer PLBUF
20:08:55.252 UTC Aug 29 2026 : IPv4 LES CEF : Et0/0 None
20:08:55.254 UTC Aug 29 2026 : IPv4 LES CEF : Et0/1 Et0/0
20:08:55.256 UTC Aug 29 2026 : IPv4 LES CEF : Et0/0 None
20:08:55.258 UTC Aug 29 2026 : IPv4 LES CEF : Et0/1 Et0/0
20:08:55.259 UTC Aug 29 2026 : IPv4 LES CEF : Et0/0 None
20:08:55.261 UTC Aug 29 2026 : IPv4 LES CEF : Et0/1 Et0/0
20:08:55.262 UTC Aug 29 2026 : IPv4 LES CEF : Et0/0 None
20:08:55.264 UTC Aug 29 2026 : IPv4 LES CEF : Et0/1 Et0/0The buffer went from 3 packets to 13. Eight of those new entries are the ping: Et0/0 None is a packet arriving, Et0/1 Et0/0 is the same packet leaving Ethernet0/1 with Ethernet0/0 named as where it came in. Four in, four out.
Debug saw zero of them. The capture point saw all of them. That is the blind spot, measured on one router in one window: debug ip packet only reports on process-switched packets, and transit traffic is CEF switched. Packets addressed to the router, like the earlier pings to 10.255.1.1, are process switched and do appear. Packets merely passing through do not.
The classic workaround is no ip route-cache on the interface to force process switching. Do not do this on a production router. You have just disabled fast forwarding, and you will cause the exact outage you were trying to avoid. Use a capture instead, either the capture point above or SPAN and ERSPAN off the switch.
One syntax note if you are copying this. The image used here carries the classic EPC syntax, with a buffer and a capture point configured separately. Hardware IOS XE platforms use the unified form, monitor capture CAP interface Ethernet0/0 both followed by monitor capture CAP match ipv4 ... and monitor capture CAP export flash:cap.pcap. Check which one your box speaks with monitor capture ? before you type the rest.
Which Tool for Which Question
The short version: capture answers "what happened to the packet", debug answers "what did the router think about it". Most troubleshooting sessions need one question, not both, and picking wrong is why people conclude debug is useless.
Cleaning Up After Yourself
A forgotten debug is a scheduled outage, and a forgotten condition is a scheduled misdiagnosis. Before you close the session, run all four of these and expect all four to come back empty:
R1#undebug all
All possible debugging has been turned off
R1#show debug
R1#show debug condition
% No conditions found
R1#monitor capture point stop PLPOINTundebug all clears conditions along with debugs, which is why the earlier capture reported 1 conditions have been removed. Do not rely on that if you removed debugs one at a time, because a condition can outlive the debug that made you set it. Check show debug condition explicitly. Capture points are separate again, and a capture point left running quietly consumes memory until someone reloads the box.
no logging console plus logging buffered. Non-negotiable.show debug condition. Zero flags triggered means silence, not scoping.show processes cpu sorted | ex 0.00 in another window. If it climbs, u all.show debug should be empty when you leave.FAQ
Why does my debug show nothing even though traffic is flowing?
Two likely causes, and they look identical. Either the traffic is CEF-switched transit, which debug ip packet cannot see, or a debug condition is in force that this debug does not evaluate. Check show debug condition first, because it takes two seconds. If the condition shows (0 flags triggered), that is your answer.
Is it ever safe to run debug on a production router?
Yes, with an ACL or condition, console logging off, and a finger on u all. The blanket "never debug in production" rule costs more outages than it prevents, because it pushes people toward guessing.
How do I stop everything immediately?
undebug all, abbreviated u all. It clears conditions as well as debugs. Learn to type it without thinking.
Does the ACL in debug ip packet 99 also drop traffic?
No. It is used purely as a match filter for the debug. It is not applied to any interface and does not affect forwarding.
Why did I get output for one interface when my condition said another?
That debug is not condition-aware for the condition type you set. Support varies per debug and per condition. Verify with show debug condition and, if in doubt, use the ACL method, which does not depend on condition awareness at all.
Does debug condition work with debug ip packet?
Do not rely on it. debug ip packet has its own filter mechanism, the numbered ACL, and that is the one to use. Mixing the two makes it much harder to reason about why you are seeing nothing.
Key Takeaways
- Debug melts routers because it punts packets to the CPU and writes to a slow console, not because debugging is inherently dangerous.
no logging consolepluslogging bufferedremoves most of the risk before you type anything else.debug condition interfaceis real and measurable: the same OSPF hello debug produced 10 lines unconditioned and 5 lines scoped, in identical time windows, on a router with two adjacencies.- A condition the debug does not understand produces zero output, with no warning. The same debug with
debug condition iplogged nothing at all.show debug conditionshowing(0 flags triggered)is the tell. debug ip packet <acl>filters without needing condition awareness. Use an extended numbered ACL, because a standard one matched the source's OSPF hellos too.- Named ACLs are rejected by
debug ip packeton IOS XE. Numbered only. debug ip packetis blind to CEF-switched transit traffic. Four pings through the router logged nothing while a capture point on the same interface caught all eight frames. Do not disable route-cache to work around it.- Capture answers "what happened to the packet". Debug answers "what did the router think about it". Pick the one that matches your question.
- Clear debugs, conditions and capture points before you log off.
Next: the IP Services cluster guide, which puts this alongside the other operational tools worth knowing on IOS XE.