Voice is tiny but merciless: 100 kbps of RTP that turns to garbage the instant it waits behind someone's file upload. QoS gives those packets a fast lane — mark them at the phone, trust the mark, and put voice in a strict-priority queue so calls stay crystal clear even when the link is full.
A VoIP call is about 80–100 kbps of small UDP/RTP packets, and it has zero tolerance for delay or loss: 150 ms of latency or a 1% drop and callers start talking over each other. The killer isn't bandwidth — a call is tiny — it's queuing delay. When the link fills up (someone uploads to the cloud, a backup kicks off), voice packets sit in the same FIFO queue behind big data packets, and the call stutters. QoS is the fix: identify voice, and let it jump the queue.
Three moves do it. Classify & mark — tag voice packets with DSCP EF (Expedited Forwarding) so every device recognizes them. Trust the boundary — let the IP phone mark, and have the switch trust that mark rather than re-inspecting. Queue with priority — a Low-Latency Queue (LLQ) that services voice strictly first, with a policer so it can't starve everything else. It's all built with the same Modular QoS CLI (MQC) — class-maps and policy-maps — you met in the firewall post. This mirrors our MikroTik QoS writeup; the concepts are identical.
01
Every packet's IP header has a DSCP field. Voice bearer traffic gets EF, call signaling gets CS3. Mark once, near the edge, and every downstream device can act on it without re-inspecting.
| Traffic | DSCP | Why |
|---|---|---|
| Voice bearer (RTP) | EF (46) | The audio itself — strict priority, lowest latency. |
| Call signaling (SIP/SCCP) | CS3 (24) | Sets up/tears down calls — important but not latency-critical. |
| Everything else | Default (0) | Best effort — web, email, file transfers. |
The golden rule of QoS: classify and mark as close to the source as possible, then trust that marking everywhere else. Re-classifying deep-packet-style at every hop is expensive and fragile; a consistent DSCP set once at the access edge lets every switch and router downstream make a cheap, instant decision. This only works if you also control who is allowed to set marks — which is the trust boundary, next.
02
A Cisco IP phone marks its own RTP as EF — but an attached PC could mark its game or torrent EF too and steal the priority lane. The switch decides whose marks to believe. Trust the phone; re-mark the PC to zero.
# Enable QoS globally (off by default on many Catalysts) SW1(config)# mls qos SW1(config)# interface GigabitEthernet0/5 SW1(config-if)# switchport access vlan 10 # data VLAN (the PC) SW1(config-if)# switchport voice vlan 110 # voice VLAN (the phone) # Trust the DSCP the phone sets — but only via the phone (CDP-verified): SW1(config-if)# mls qos trust device cisco-phone SW1(config-if)# mls qos trust dscp # The PC hangs off the phone's switch port; the phone RE-MARKS the PC's # traffic to DSCP 0, so a PC can't fake EF and jump the voice queue.
With mls qos globally enabled, ports default to untrusted — they rewrite incoming DSCP to 0. So enabling QoS without setting trust can make voice worse: the phone marks EF, the switch wipes it to 0, and downstream LLQ never matches. Either extend the trust boundary correctly (trust the phone at the edge) or explicitly re-mark voice yourself with a policy. Verify with show mls qos interface gi0/5 — it tells you the trust state and whether marks survive.
03
On the router that will queue, define classes that match the DSCP values the edge already set. Matching a trusted mark is cheap; matching by ACL/port is the fallback when you can't trust the source.
# Preferred: match the DSCP the edge already marked R1(config)# class-map match-all VOICE R1(config-cmap)# match dscp ef R1(config-cmap)# exit R1(config)# class-map match-any SIGNALING R1(config-cmap)# match dscp cs3 R1(config-cmap)# match dscp af31 R1(config-cmap)# exit # Fallback if you must classify by port instead of trusting a mark: # match ip rtp 16384 16383 ← the standard RTP UDP port range
04
This is the heart of voice QoS. A Low-Latency Queue serves voice before everything else, every time — but with a bandwidth cap (a built-in policer) so a flood of voice can't starve the link. Everything else shares what's left by weight.
R1(config)# policy-map WAN-QOS R1(config-pmap)# class VOICE R1(config-pmap-c)# priority percent 20 # LLQ: strict priority, capped at 20% R1(config-pmap-c)# exit R1(config-pmap)# class SIGNALING R1(config-pmap-c)# bandwidth percent 5 # guaranteed 5%, not strict-priority R1(config-pmap-c)# exit R1(config-pmap)# class class-default R1(config-pmap-c)# fair-queue # everyone else shares fairly R1(config-pmap-c)# random-detect dscp-based # WRED: drop early, avoid TCP sync R1(config-pmap-c)# end # Attach it OUTBOUND on the WAN interface — QoS acts on egress congestion R1(config)# interface GigabitEthernet0/0 R1(config-if)# service-policy output WAN-QOS R1(config-if)# end
The priority queue is strict — it will happily starve everything else if you let it. The rule of thumb is to cap voice at no more than a third of the link (20–33%), sized to your real call count: roughly 100 kbps per G.711 call plus overhead. Over-provisioning the priority class means a burst of voice (or anything mismarked as EF) can choke data and even routing keepalives. The policer built into priority protects the rest of the link — but only if you set it sanely.
QoS only engages when the interface queue fills. On a sub-line-rate WAN (a 50/10 Mbps service on a GigE handoff), the router's physical interface never looks "full," so the queue never forms and your policy never triggers — the ISP drops your packets instead, with no QoS. The fix is a parent shaper: shape average to your real contracted rate in an outer policy, with WAN-QOS nested inside it. This hierarchical (parent-shape, child-queue) policy is how QoS is done on nearly every real internet circuit — worth its own deep-dive.
05
QoS is invisible until the link is busy. The one command that matters shows per-class packet counts, drops, and — critically — whether the priority queue is dropping anything (it never should).
R1# show policy-map interface GigabitEthernet0/0 Class-map: VOICE (match-all) 2841000 packets Priority: 20% (2000 kbps), burst 50000, b/w exceed drops: 0 ↑ MUST be 0 — voice never dropped Class-map: SIGNALING (match-any) 14200 packets bandwidth 5% (500 kbps) Class-map: class-default 8100000 packets queue limit 64 packets (drops) 24710 ← data drops under load: EXPECTED & fine # The whole point in one screen: voice "exceed drops: 0" while data drops # during congestion. That's QoS working — the right traffic waits.
| Command | Answers |
|---|---|
| show policy-map interface g0/0 | Per-class counts and drops — the money command. Voice drops must be 0. |
| show mls qos interface g0/5 | Switch-side trust state — are the phone's marks surviving? |
| show policy-map WAN-QOS | The configured policy, as parsed. |
| show class-map | What each class matches. |
06
Our MikroTik QoS post solves the identical problem with queue trees and mangle marks. The DSCP values are universal; only the queuing engine differs.
| MikroTik / RouterOS | Cisco IOS |
|---|---|
| mangle mark-packet by port/DSCP | class-map match dscp / match ip rtp |
| Set DSCP in mangle (change-dscp) | set dscp ef in a policy-map |
| Queue tree, priority=1 for voice | LLQ priority percent 20 |
| max-limit on the voice queue | The policer built into priority |
| Parent queue = link speed | Parent shape average (HQoS) |
| bridge port trust for phone marks | mls qos trust device cisco-phone |
Takeaways
priority serves voice strictly first, with a built-in policer so it can't starve the link. Cap it at ~20–33%, sized to real call count.shape average at your real contracted rate.show policy-map interface — voice "exceed drops: 0" while data drops during congestion is QoS working exactly as designed.NOCTIS designs QoS for hotels and SMBs running voice over the same link as everything else — proper marking, trust boundaries, and priority queuing tuned to your call volume. On Cisco or MikroTik, verified under real load.
Book a Discovery Call →