QoS concept — traffic classes, scheduling, the QoS pipeline
What you'll learn
- Define what QoS is and what problem it solves
- Explain the QoS pipeline (classification, marking, queuing, scheduling)
- Distinguish bandwidth shaping from queuing (scheduling)
- Configure a basic QoS policy on VyOS 1.5 LTS
Prerequisites
None — start here.
Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-15
Quality of Service (QoS) is the set of mechanisms for managing how traffic is handled when network resources are constrained. A 1 Gbps link can carry ~1 Gbps of traffic; when traffic exceeds this, congestion occurs. Without QoS, packets are dropped (typically the most recent ones, in tail-drop). With QoS, the router classifies traffic into classes, marks packets with priority, queues packets per class, and schedules the transmission across classes to give preferential treatment to latency-sensitive traffic (voice, video, interactive) over bulk traffic (file transfers, backups).
This lesson covers what QoS is, the QoS pipeline (classification, marking, queuing, scheduling), the scheduling algorithms (FIFO, priority, WFQ, HTB, fq_codel), and the production patterns.
What QoS solves
A network has finite resources: bandwidth, buffer space, CPU. When traffic exceeds these resources, congestion occurs. Without QoS, the router treats all packets equally (typically FIFO with tail-drop); latency-sensitive traffic (voice, video) suffers alongside bulk traffic (file transfers).
QoS gives priority to latency-sensitive traffic:
- Voice over IP (VoIP): requires low latency (< 100 ms) and low jitter. A 50 ms delay on a voice packet is noticeable.
- Video conferencing: requires low latency and consistent bandwidth.
- Interactive (SSH, RDP): benefits from low latency.
- Bulk (file transfers, backups): tolerates higher latency.
flowchart LR
subgraph "Without QoS"
P1["VoIP packet<br/>latency critical"]
P2["Video packet<br/>high bandwidth"]
P3["Bulk transfer<br/>low priority"]
OUT["Output queue<br/>(FIFO, tail-drop)"]
P1 --> OUT
P2 --> OUT
P3 --> OUT
end
subgraph "With QoS"
Q1["High priority queue"]
Q2["Medium priority queue"]
Q3["Low priority queue"]
SCHED["Scheduler<br/>(strict priority or HTB)"]
Q1 --> SCHED
Q2 --> SCHED
Q3 --> SCHED
end
The QoS pipeline:
- Classification — determine the traffic class for each packet (e.g., VoIP → class A; web → class B; bulk → class C).
- Marking — set the DSCP (Differentiated Services Code Point) in the IP header, or set a Linux internal priority (
skb->priority). - Queuing — place the packet in the appropriate queue.
- Scheduling — decide which queue to serve next; honour bandwidth limits.
Scheduling algorithms
The Linux kernel supports several scheduling algorithms:
FIFO (First In, First Out)
The simplest: one queue, packets are served in order. No QoS benefit; no class distinction. Used when QoS is not configured.
Priority queuing (prio)
Multiple queues with strict priority. The high-priority queue is served before the medium-priority queue; medium before low. Simple but can starve low-priority queues if high-priority is always busy.
Weighted fair queuing (WFQ)
Packets are classified and each class gets a share of the bandwidth. If a class has no traffic, its share is distributed to other classes. Fair but may not prioritize latency-sensitive traffic enough.
HTB (Hierarchical Token Bucket)
A hierarchical shaper/queuing algorithm. Each class has a guaranteed rate (rate) and a ceiling (ceil); within the class, packets are queued and shaped. HTB allows hierarchical classification (e.g., a “customer” class with sub-classes for “voice” and “data”).
HTB is the typical production choice: it provides guaranteed bandwidth per class, hierarchical classification, and shaping.
fq_codel (Fair Queuing Controlled Delay)
A modern scheduler that combines fair queuing with controlled delay (CoDel) AQM (Active Queue Management). Each flow gets its own queue; queues are served in a round-robin manner; CoDel drops packets from queues with excessive delay.
fq_codel is the typical choice for the leaf scheduler (the bottom of the HTB hierarchy): it provides fairness and low latency without complex configuration.
flowchart TB
ROOT["Root class<br/>rate = link capacity"]
HIGH["High priority class<br/>rate = 200 Mbps"]
LOW["Low priority class<br/>rate = 800 Mbps"]
HIGHF["fq_codel leaf"]
LOWF["fq_codel leaf"]
ROOT --> HIGH
ROOT --> LOW
HIGH --> HIGHF
LOW --> LOWF
A typical QoS tree:
- Root class: the total link capacity.
- High priority class (e.g., voice): limited to a portion of the link.
- Low priority class: the remainder.
- Each class uses fq_codel as the leaf scheduler.
Shaping vs policing
Two related but distinct mechanisms:
Shaping queues packets to smooth traffic and rate-limit. The shaped traffic conforms to a rate limit; bursts are absorbed by the queue. Shaping is typically done outbound (on the egress interface).
Policing drops packets that exceed a rate limit. There is no queue; packets are simply dropped when the rate is exceeded. Policing is typically used for ingress (rate-limit at the network boundary).
The difference:
- Shaping: smooth, no drops, but adds latency (the queue introduces delay).
- Policing: aggressive, drops excess, no additional latency.
The discipline: use shaping outbound to smooth traffic to the upstream’s rate limit; use policing inbound to enforce the customer’s rate limit.
Configuration on VyOS 1.5 LTS
A basic QoS policy on VyOS:
configure
# Define a QoS policy
set qos policy shaper WAN-OUT default bandwidth 1gbit
set qos policy shaper WAN-OUT default burst 15k
set qos policy shaper WAN-OUT default queue-type fq-codel
# Class for VoIP
set qos policy shaper WAN-OUT class voice match voip
set qos policy shaper WAN-OUT class voice priority 0
set qos policy shaper WAN-OUT class voice queue-type fq-codel
set qos policy shaper WAN-OUT class voice ceiling 200mbit
# Class for bulk
set qos policy shaper WAN-OUT class bulk match all
set qos policy shaper WAN-OUT class bulk priority 7
set qos policy shaper WAN-OUT class bulk queue-type fq-codel
# Apply the policy to the WAN interface (egress)
set interfaces ethernet eth0 traffic-policy out WAN-OUT
commit
save
The configuration:
- Default bandwidth: 1 Gbps (the link capacity).
- Voice class: high priority (priority 0), ceiling 200 Mbps.
- Bulk class: low priority (priority 7), default.
- Apply to WAN egress: the policy is enforced on outgoing traffic.
The traffic enters the HTB tree; the filter matches the traffic to classes; the classes queue and shape; the leaf scheduler (fq_codel) provides fairness.
Validation
# Inspect the QoS state
show qos
# Lists the policies and classes
# Detailed class state
tc class show dev eth0
tc -s class show dev eth0
# Lists the classes with statistics
# Inspect the leaf queues
tc qdisc show dev eth0
# Lists the qdiscs (queues and schedulers)
# Detailed queue statistics
tc -s qdisc show dev eth0
# Lists the packets/bytes per queue
# Test with iPerf3
iperf3 -c <server> -P 4
# Should achieve close to the bandwidth limit
A clean validation: the QoS policy is applied to the interface; traffic is classified correctly; iPerf3 throughput is close to the limit; voice traffic has low latency under congestion.
Production failure modes
Wrong class assignment
Traffic is misclassified (e.g., bulk traffic is in the voice class). The bulk traffic consumes the voice class’s bandwidth; voice suffers.
Diagnostic: tc -s class show dev eth0 shows the voice class has bulk traffic.
Fix: correct the match criteria.
Shape below the actual rate
The shaper is set below the link’s actual bandwidth (e.g., 100 Mbps on a 1 Gbps link). Throughput is artificially limited.
Fix: increase the bandwidth to the actual link capacity.
Leaf scheduler wrong choice
SFQ (Stochastic Fairness Queuing) is used instead of fq_codel. SFQ has known issues with hash collisions; flows may be unfair.
Fix: use fq_codel as the leaf.
FQ codel thresholds wrong
fq_codel’s target and interval are too high. Latency is not properly controlled.
Fix: tune the target (typical: 5 ms) and interval (typical: 100 ms).
Rollback
# Enter configuration mode and write the running configuration to a
# file you can load back. `save` is a configuration-mode command that
# takes a path; operational mode has no `| save` pipe.
configure
save /config/pre-change-qos-TICKET.conf
# Remove the QoS policy
delete qos policy shaper WAN-OUT
delete interfaces ethernet eth0 traffic-policy out WAN-OUT
# Read the diff before committing anything
compare
commit
# Or restore a previous configuration
load /config/pre-change-qos-TICKET.conf
commit
save
The rollback removes the QoS policy; the interface reverts to FIFO (no QoS).
Production discipline
Cross-course references
- Part XLV-02 (
XLV-VyOS-QoS/ DSCP marking) covers DSCP marking, a related QoS concept. - Part XLV-03 (
XLV-VyOS-QoS/ classification) covers packet classification. - Part XLV-04 (
XLV-VyOS-QoS/ queuing and shaping) covers the scheduler details.
Quiz
Knowledge check · 4 questions
Q1. What is the role of `fq_codel` in a Linux QoS configuration?
Q2. Shaping provides extra bandwidth beyond the link's actual capacity by intelligently queuing packets.
Q3. An operator deploys QoS with priority queuing (prio). The high-priority queue is consistently full. The low-priority queue is starved; bulk transfers never complete. What is the issue?
The QoS configuration uses priority queuing (prio) with three classes: high, medium, low. The high-priority class is always full (voice traffic is sustained at high rates). Because prio uses strict priority, the medium and low queues are not served while the high queue has packets. Bulk transfers (low priority) do not progress; they experience extreme latency or starvation.
Q4. An operator tests a QoS configuration. Voice traffic has low latency under congestion. Bulk traffic is rate-limited correctly. However, during a brief congestion event, voice traffic experiences high jitter (varying latency). What is the issue?
The QoS configuration has a voice class (high priority) and a bulk class. Under sustained load, voice traffic has low latency. But during a brief congestion event (e.g., a TCP connection starts), voice traffic experiences high jitter. The leaf scheduler (SFQ or default fifo) does not control latency variation effectively during transient congestion.
Passing score: 75%. Answers are checked in this browser.