WAN Optimization for Latency-Sensitive Applications (POS, VoIP, Video)
A retail store with a 20 Mbps broadband connection and a 4G backup is running POS terminals, CCTV upload, a firmware update, and a staff member streaming music. Without traffic prioritization, a POS transaction — the thing that actually makes money — gets the same treatment as every other packet. That is a problem.
This post walks through how we configure QoS on Hopbox SD-WAN devices, from traffic classification to queue disciplines, with real config snippets from OpenWrt.
Why QoS Matters for Retail and Enterprise
Section titled “Why QoS Matters for Retail and Enterprise”Bandwidth is not the bottleneck. Latency and jitter are.
A POS terminal sending a payment authorization request generates maybe 2–5 KB of data. On a 10 Mbps link, that takes less than a millisecond to transmit. But if that packet sits behind 500 KB of CCTV footage in a queue, it waits. If the link is saturated by a firmware download, the POS packet competes for buffer space and may be dropped, triggering a TCP retransmit.
The result: a transaction that should take 200ms takes 2 seconds. Or times out. The cashier retries. The customer waits. Multiply by hundreds of transactions per day across hundreds of stores.
QoS ensures that latency-sensitive traffic (POS, VoIP, video conferencing) gets priority access to the wire, regardless of what else is happening on the link.
Traffic Classification
Section titled “Traffic Classification”Before you can prioritize traffic, you need to identify it. There are three main approaches, each with trade-offs.
Port-Based Classification
Section titled “Port-Based Classification”The simplest method. POS terminals talk to payment gateways on known ports. VoIP uses SIP (5060/5061) and RTP (dynamic range, typically 10000–20000).
# nftables rules for traffic classification by porttable inet qos_classify { chain prerouting { type filter hook prerouting priority -150; policy accept;
# POS traffic — payment gateway ports tcp dport { 443, 8443 } ip saddr 192.168.10.0/24 meta priority set 1:10
# VoIP signaling udp dport { 5060, 5061 } meta priority set 1:10
# VoIP media (RTP) udp dport 10000-20000 meta priority set 1:20
# CCTV upload — known NVR destinations tcp dport 554 meta priority set 1:30 ip daddr 10.100.0.0/16 tcp dport 443 meta priority set 1:30
# Everything else — default priority meta priority set 1:40 }}Port-based classification is fast and deterministic, but fragile. If the payment gateway changes its port, or if POS traffic is tunneled over a generic HTTPS connection, port matching misses it.
DSCP Marking
Section titled “DSCP Marking”Differentiated Services Code Point (DSCP) uses 6 bits in the IP header’s ToS field to indicate traffic class. If your POS application or VoIP phone sets DSCP on outgoing packets, the Hopbox can classify based on those markings.
# Common DSCP values# EF (46) — Expedited Forwarding: VoIP media# AF41 (34) — Assured Forwarding: video conferencing# AF21 (18) — Assured Forwarding: POS/transactional# CS1 (8) — Scavenger: bulk/background traffic# BE (0) — Best Effort: default
# nftables: classify based on DSCPip dscp ef meta priority set 1:10ip dscp af41 meta priority set 1:20ip dscp af21 meta priority set 1:15ip dscp cs1 meta priority set 1:40The advantage of DSCP is that the application itself declares its priority. The disadvantage is that most applications do not set DSCP, and ISPs frequently strip DSCP markings at their edge. Within your own SD-WAN overlay (over WireGuard tunnels), DSCP markings are preserved — the ISP only sees the outer WireGuard UDP packets.
Source-Based Classification
Section titled “Source-Based Classification”In retail deployments, the simplest and most reliable approach is often source-based. POS terminals live on VLAN 10. CCTV cameras on VLAN 20. Staff devices on VLAN 30. Classification by source subnet:
# Classify by source VLAN/subnetip saddr 192.168.10.0/24 meta priority set 1:10 # POS VLANip saddr 192.168.20.0/24 meta priority set 1:30 # CCTV VLANip saddr 192.168.30.0/24 meta priority set 1:40 # Staff VLANThis is what we use most often. It is not elegant, but it is robust. A POS terminal cannot accidentally end up in the wrong traffic class because it is physically (or logically via VLAN) on the priority network.
Queue Disciplines on OpenWrt
Section titled “Queue Disciplines on OpenWrt”Classification assigns packets to classes. Queue disciplines decide how those classes share the available bandwidth.
fq_codel: The Sensible Default
Section titled “fq_codel: The Sensible Default”Fair Queueing with Controlled Delay (fq_codel) is the default queue discipline on modern Linux kernels and OpenWrt. It combats bufferbloat by keeping queue depth low and fairly distributing bandwidth across flows.
# Check current qdisc on an interfaceroot@hopbox:~# tc qdisc show dev eth0qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 4Mb ecn drop_batch 64fq_codel is excellent for general fairness but does not provide strict prioritization. A POS packet and a firmware download packet get “fair” treatment — which is not what we want.
CAKE: The Better Default
Section titled “CAKE: The Better Default”Common Applications Kept Enhanced (cake) is an evolution of fq_codel designed specifically for home and small-office routers. It includes built-in traffic shaping, per-host fairness, and DSCP-based priority tiers.
# Apply CAKE with bandwidth shaping and priority tierstc qdisc replace dev eth0 root cake bandwidth 20mbit diffserv4 nat wash
# diffserv4 creates 4 tiers based on DSCP:# Tin 0 (Bulk): CS1, LE — 1/16 bandwidth share# Tin 1 (Best Effort): CS0, default — 4/16 bandwidth share# Tin 2 (Video): CS2-CS5, AFxx — 4/16 bandwidth share# Tin 3 (Voice): CS6, CS7, EF — 1/16 bandwidth share (but strict priority)The bandwidth parameter is critical. Set it to slightly below your actual link speed (e.g., 18mbit for a 20 Mbps link). This ensures the queue is on your device, not in your ISP’s buffer — giving you control over what gets dropped and what gets prioritized.
Hierarchical Token Bucket (HTB) for Fine-Grained Control
Section titled “Hierarchical Token Bucket (HTB) for Fine-Grained Control”When CAKE’s four tiers are not enough, we use HTB with explicit class hierarchy:
# HTB setup with guaranteed minimums per classtc qdisc add dev eth0 root handle 1: htb default 40
# Root class — total link bandwidthtc class add dev eth0 parent 1: classid 1:1 htb rate 20mbit ceil 20mbit
# POS traffic — guaranteed 5mbit, can burst to full linktc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit ceil 20mbit prio 0tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
# VoIP — guaranteed 2mbit, strict prioritytc class add dev eth0 parent 1:1 classid 1:20 htb rate 2mbit ceil 5mbit prio 1tc qdisc add dev eth0 parent 1:20 handle 20: fq_codel
# CCTV — guaranteed 8mbit, lower prioritytc class add dev eth0 parent 1:1 classid 1:30 htb rate 8mbit ceil 15mbit prio 2tc qdisc add dev eth0 parent 1:30 handle 30: fq_codel
# Everything else — gets whatever is lefttc class add dev eth0 parent 1:1 classid 1:40 htb rate 2mbit ceil 20mbit prio 3tc qdisc add dev eth0 parent 1:40 handle 40: fq_codelThe key design choices:
- POS gets
prio 0— highest priority scheduling. When POS packets are in the queue, they go first. - POS
rateis 5mbit — even under full congestion, POS traffic gets at least 5 Mbps. It does not need it (POS is low-bandwidth), but the guaranteed allocation means it never starves. - POS
ceilis 20mbit — it can use the full link if no one else needs it. - CCTV gets 8mbit guaranteed — enough for continuous upload, but capped at 15mbit so it does not saturate the link.
- Each leaf class uses
fq_codel— fair queueing within each priority tier, preventing a single flow from dominating its class.
VoIP Jitter Budget
Section titled “VoIP Jitter Budget”VoIP is uniquely sensitive to jitter. It is not just latency — it is variation in latency. A consistent 100ms one-way delay is fine for voice; a delay that swings between 50ms and 200ms is not.
The ITU-T G.114 recommendation sets a one-way delay target of 150ms for voice. The typical jitter budget breakdown:
Component Budget━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Codec processing 20msJitter buffer 40msNetwork transit 60msDe-jitter + playback 30ms━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Total 150msNetwork transit gets 60ms. If your WAN link has 30ms baseline latency, you have 30ms of headroom for jitter. If jitter exceeds that, the jitter buffer underruns and you hear choppy audio.
QoS policies for VoIP must:
- Guarantee low queue delay — strict priority scheduling, small queue depth
- Limit competing traffic — shape bulk traffic below link capacity so VoIP never sits behind a full buffer
- Minimize packet loss — VoIP uses UDP; a lost packet is a gap in audio, not a retransmit
Measuring Improvement
Section titled “Measuring Improvement”QoS configuration without measurement is guesswork. We measure before and after applying policies:
# Measure bufferbloat with the ICMP-under-load test# Terminal 1: saturate the linkiperf3 -c speedtest.hopbox.in -t 30 -P 4
# Terminal 2: measure latency during saturationping -c 30 8.8.8.8
# Without QoS (typical):# round-trip min/avg/max = 12.4/347.2/892.1 ms
# With CAKE shaping (typical):# round-trip min/avg/max = 12.8/18.4/31.2 msThe difference is dramatic. Without QoS, latency under load spikes to nearly a second — that is a POS transaction timeout. With CAKE shaping the link to just below capacity, latency stays within 30ms even under full saturation.
Continuous Monitoring
Section titled “Continuous Monitoring”Every Hopbox device reports per-class queue statistics to Prometheus:
# Exported metricshopbox_tc_class_bytes_total{class="pos",interface="eth0"} 1248576hopbox_tc_class_packets_total{class="pos",interface="eth0"} 8724hopbox_tc_class_drops_total{class="pos",interface="eth0"} 0hopbox_tc_class_overlimits_total{class="bulk",interface="eth0"} 42891If drops_total for the POS class ever goes above zero, we have a problem — it means even the guaranteed bandwidth allocation is insufficient, or classification is misassigning traffic. This triggers an alert for the NOC to investigate.
Putting It All Together
Section titled “Putting It All Together”A complete QoS configuration for a retail Hopbox site involves:
- VLAN segmentation — POS, CCTV, staff on separate VLANs
- nftables classification — source-subnet-based, marking packets with class IDs
- CAKE or HTB shaping — on the WAN egress interface, shaped to 90% of measured link speed
- Per-link tuning — primary fiber link gets different shaping than 4G backup (different bandwidth, different latency characteristics)
- Monitoring — per-class drop/queue metrics exported to Prometheus
- Ansible-managed — QoS configs generated from site inventory, pushed via management plane
The result: a POS transaction completes in under 200ms even when the store’s CCTV system is uploading footage and an employee is watching a training video. The network does not need more bandwidth — it needs better scheduling.