QoS on VyOS: Making Latency Feel Better

QoS (Quality of Service) is often misunderstood. People expect it to “make the internet faster.” It doesn’t. QoS is about managing scarcity — when there’s not enough bandwidth for everyone, QoS decides who gets priority.

The key insight: QoS only works when you control the bottleneck. If your ISP is the bottleneck, traffic shaping on your router shapes what leaves your network, not what your ISP does. Understanding this is crucial for effective QoS.

Understanding the Problem: Bufferbloat

Modern networks have a hidden enemy: bufferbloat. Network devices have large buffers that queue packets when congested. Large buffers = high latency during congestion.

Scenario: You’re on a video call. Someone starts a large download. Suddenly your call has 500ms latency because packets are stuck in buffers behind download packets.

QoS solves this by:

  1. Shaping traffic below the actual link speed (to control where queuing happens)
  2. Prioritizing latency-sensitive traffic
  3. Using smart queue disciplines that prevent buffer buildup

Measuring the Problem

Before configuring QoS, measure your baseline:

Terminal window
# Test bufferbloat (from client, while running a speed test)
ping 8.8.8.8
# Watch for latency increase during upload/download
# Normal: ~20ms, Bufferbloat: 200-1000ms

Or use the Bufferbloat test — it specifically measures latency under load.

VyOS Traffic Shaping Basics

VyOS uses Linux tc (traffic control) under the hood. Two main components:

Shaper: Limits overall bandwidth Classes/Queues: Divide bandwidth among traffic types

Terminal window
configure
# Basic traffic shaping on WAN interface
set traffic-policy shaper WAN-OUT bandwidth '95mbit'
set traffic-policy shaper WAN-OUT default bandwidth '50%'
set traffic-policy shaper WAN-OUT default ceiling '100%'
set traffic-policy shaper WAN-OUT default queue-type 'fq-codel'
# Apply to outbound on WAN interface
set interfaces ethernet eth0 traffic-policy out 'WAN-OUT'
commit

Key points:

  • bandwidth ‘95mbit’: Total shaper bandwidth. Set this ~95% of your actual upload speed
  • fq-codel: Fair Queue with Controlled Delay — fights bufferbloat
  • ceiling ‘100%’: Can burst to full bandwidth if available

Why 95% of Your Actual Speed?

If your upload is 100Mbps and you shape at 100Mbps, congestion still happens at your ISP’s edge. You’re not controlling the bottleneck.

Shape at 95Mbps (or even 90Mbps for very stable latency), and congestion happens at your router, where you control the queue. Your router’s smart queue (fq-codel) manages latency instead of your ISP’s dumb FIFO buffer.

Traffic Classes: Prioritizing Different Traffic

Terminal window
configure
# Create shaper with classes
set traffic-policy shaper WAN-OUT bandwidth '95mbit'
# Voice/Video - highest priority, guaranteed bandwidth
set traffic-policy shaper WAN-OUT class 10 bandwidth '20%'
set traffic-policy shaper WAN-OUT class 10 ceiling '50%'
set traffic-policy shaper WAN-OUT class 10 priority '0'
set traffic-policy shaper WAN-OUT class 10 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 10 match VOIP ip dscp 'ef'
# Interactive (SSH, gaming) - high priority
set traffic-policy shaper WAN-OUT class 20 bandwidth '10%'
set traffic-policy shaper WAN-OUT class 20 ceiling '100%'
set traffic-policy shaper WAN-OUT class 20 priority '1'
set traffic-policy shaper WAN-OUT class 20 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 20 match SSH ip protocol 'tcp'
set traffic-policy shaper WAN-OUT class 20 match SSH ip destination port '22'
# Web browsing - normal priority
set traffic-policy shaper WAN-OUT class 30 bandwidth '30%'
set traffic-policy shaper WAN-OUT class 30 ceiling '100%'
set traffic-policy shaper WAN-OUT class 30 priority '3'
set traffic-policy shaper WAN-OUT class 30 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 30 match HTTP ip protocol 'tcp'
set traffic-policy shaper WAN-OUT class 30 match HTTP ip destination port '80,443'
# Bulk downloads - lowest priority
set traffic-policy shaper WAN-OUT class 40 bandwidth '20%'
set traffic-policy shaper WAN-OUT class 40 ceiling '90%'
set traffic-policy shaper WAN-OUT class 40 priority '5'
set traffic-policy shaper WAN-OUT class 40 queue-type 'fq-codel'
# Default for unclassified traffic
set traffic-policy shaper WAN-OUT default bandwidth '20%'
set traffic-policy shaper WAN-OUT default ceiling '100%'
set traffic-policy shaper WAN-OUT default priority '4'
set traffic-policy shaper WAN-OUT default queue-type 'fq-codel'
set interfaces ethernet eth0 traffic-policy out 'WAN-OUT'
commit

Understanding the Parameters

bandwidth: Guaranteed minimum bandwidth for this class ceiling: Maximum bandwidth when other classes aren’t using theirs priority: Lower number = higher priority (0 is highest) queue-type: Algorithm for managing the queue

Bandwidth percentages should roughly add up to 100%. The ceiling allows classes to borrow unused bandwidth.

Queue Types: fq-codel vs Others

fq-codel (Fair Queue Controlled Delay): Best for most cases. Maintains low latency, fair sharing between flows. Use this unless you have specific needs.

sfq (Stochastic Fair Queue): Simpler, less effective at latency control. Legacy option.

pfifo/bfifo: Simple FIFO queues. Don’t fight bufferbloat. Avoid.

cake: Advanced shaper (may need additional packages). Even better than fq-codel for some scenarios.

Terminal window
# If cake is available
set traffic-policy shaper WAN-OUT default queue-type 'cake'

Inbound Shaping: The Hard Problem

You can’t directly control inbound traffic — it’s already at your doorstep. But you can:

  1. Police incoming traffic: Drop/mark packets exceeding rate
  2. Use ingress shaping: Shape traffic after it arrives
  3. Rely on TCP feedback: Shaping outbound ACKs affects inbound TCP rate
Terminal window
configure
# Ingress policing on WAN interface
set traffic-policy limiter WAN-IN class 10 bandwidth '95mbit'
set traffic-policy limiter WAN-IN class 10 match ALL ip source address '0.0.0.0/0'
set traffic-policy limiter WAN-IN default bandwidth '95mbit'
set interfaces ethernet eth0 traffic-policy in 'WAN-IN'
commit

This is less precise than outbound shaping. For better download QoS, shape slightly below your download speed and let fq-codel manage queuing.

Practical Examples

Home Office: Prioritize Video Calls

Terminal window
# Identify video call traffic (Zoom, Teams, etc use UDP on various ports)
set traffic-policy shaper WAN-OUT class 10 match VIDEO-UDP ip protocol 'udp'
set traffic-policy shaper WAN-OUT class 10 match VIDEO-UDP ip destination port '3478-3481,8801-8810,19302-19309'
# Give video 30% guaranteed, can burst to 60%
set traffic-policy shaper WAN-OUT class 10 bandwidth '30%'
set traffic-policy shaper WAN-OUT class 10 ceiling '60%'
set traffic-policy shaper WAN-OUT class 10 priority '0'

Gaming: Low Latency

Terminal window
# Gaming often uses specific ports (varies by game)
set traffic-policy shaper WAN-OUT class 15 match GAMING ip protocol 'udp'
set traffic-policy shaper WAN-OUT class 15 match GAMING ip source port '1024-65535'
set traffic-policy shaper WAN-OUT class 15 bandwidth '15%'
set traffic-policy shaper WAN-OUT class 15 ceiling '50%'
set traffic-policy shaper WAN-OUT class 15 priority '0'
# Also prioritize small packets (often game updates)
set traffic-policy shaper WAN-OUT class 15 match SMALL ip ip-length '<256'

Torrent/Backup Deprioritization

Terminal window
# Bulk traffic class - low priority
set traffic-policy shaper WAN-OUT class 50 bandwidth '10%'
set traffic-policy shaper WAN-OUT class 50 ceiling '80%'
set traffic-policy shaper WAN-OUT class 50 priority '7'
# Match by ports commonly used by bulk transfers
set traffic-policy shaper WAN-OUT class 50 match TORRENT ip destination port '6881-6889'

DSCP Marking

DSCP (Differentiated Services Code Point) is a field in IP header used to classify traffic. Many applications set DSCP; you can use it for classification:

Terminal window
# Match on DSCP values set by applications
set traffic-policy shaper WAN-OUT class 10 match VOICE ip dscp 'ef'
set traffic-policy shaper WAN-OUT class 20 match VIDEO ip dscp 'af41'

Common DSCP values:

  • EF (46): Expedited Forwarding - voice
  • AF41 (34): Assured Forwarding - video
  • AF21 (18): Assured Forwarding - business critical
  • CS1 (8): Scavenger - bulk/background

You can also mark traffic yourself:

Terminal window
# Mark VoIP traffic with EF
set firewall ipv4 name MARK-QOS rule 10 action 'accept'
set firewall ipv4 name MARK-QOS rule 10 protocol 'udp'
set firewall ipv4 name MARK-QOS rule 10 destination port '5060'
set firewall ipv4 name MARK-QOS rule 10 set dscp 'ef'

Monitoring QoS

Terminal window
# Show current traffic policy statistics
show queueing interface eth0
# Show class statistics
tc -s class show dev eth0
# Watch queue lengths
watch tc -s qdisc show dev eth0

Look for:

  • drops: Some drops are normal (fq-codel drops to signal congestion)
  • backlogs: Should be low, high backlog = buffer building up
  • overlimits: Traffic exceeding class bandwidth (borrowing from ceiling)

Debugging QoS Issues

Traffic Not Being Classified

Terminal window
# Most traffic in default class? Check your matches
show queueing interface eth0

If priority traffic isn’t getting classified, verify:

  • Port/protocol matches are correct
  • Traffic isn’t using unexpected ports (HTTPS multiplexes everything over 443)
  • Match rules are specific enough

Latency Still High

  1. Shaper bandwidth too high: Lower it (try 90% of link speed)
  2. Not using fq-codel: Change queue-type
  3. Inbound is the problem: Need ingress shaping too
  4. ISP QoS: Your ISP might have their own queuing

VoIP Quality Still Poor

  1. Jitter buffer: Some jitter is handled by endpoints
  2. Packet loss: Check show queueing for excessive drops
  3. Misclassified traffic: Verify VoIP is hitting the right class

Complete QoS Configuration

Terminal window
# === Traffic Policy ===
set traffic-policy shaper WAN-OUT bandwidth '95mbit'
# Voice/Video - highest priority
set traffic-policy shaper WAN-OUT class 10 bandwidth '20%'
set traffic-policy shaper WAN-OUT class 10 ceiling '50%'
set traffic-policy shaper WAN-OUT class 10 priority '0'
set traffic-policy shaper WAN-OUT class 10 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 10 match VOIP ip dscp 'ef'
set traffic-policy shaper WAN-OUT class 10 match REALTIME ip protocol 'udp'
set traffic-policy shaper WAN-OUT class 10 match REALTIME ip destination port '3478-3481,5060,16384-32767'
# Interactive
set traffic-policy shaper WAN-OUT class 20 bandwidth '15%'
set traffic-policy shaper WAN-OUT class 20 ceiling '100%'
set traffic-policy shaper WAN-OUT class 20 priority '1'
set traffic-policy shaper WAN-OUT class 20 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 20 match SSH ip protocol 'tcp'
set traffic-policy shaper WAN-OUT class 20 match SSH ip destination port '22'
set traffic-policy shaper WAN-OUT class 20 match DNS ip protocol 'udp'
set traffic-policy shaper WAN-OUT class 20 match DNS ip destination port '53'
# Web
set traffic-policy shaper WAN-OUT class 30 bandwidth '35%'
set traffic-policy shaper WAN-OUT class 30 ceiling '100%'
set traffic-policy shaper WAN-OUT class 30 priority '3'
set traffic-policy shaper WAN-OUT class 30 queue-type 'fq-codel'
set traffic-policy shaper WAN-OUT class 30 match WEB ip protocol 'tcp'
set traffic-policy shaper WAN-OUT class 30 match WEB ip destination port '80,443'
# Bulk
set traffic-policy shaper WAN-OUT class 40 bandwidth '10%'
set traffic-policy shaper WAN-OUT class 40 ceiling '80%'
set traffic-policy shaper WAN-OUT class 40 priority '6'
set traffic-policy shaper WAN-OUT class 40 queue-type 'fq-codel'
# Default
set traffic-policy shaper WAN-OUT default bandwidth '20%'
set traffic-policy shaper WAN-OUT default ceiling '100%'
set traffic-policy shaper WAN-OUT default priority '4'
set traffic-policy shaper WAN-OUT default queue-type 'fq-codel'
# === Apply ===
set interfaces ethernet eth0 traffic-policy out 'WAN-OUT'

The Lesson

QoS works when you understand the bottleneck:

  1. Shape below link speed: This moves the bottleneck to your router where you control queuing
  2. Use smart queues (fq-codel): They maintain low latency automatically
  3. Prioritize appropriately: Not everything can be high priority — that’s just no priority

The goal isn’t faster internet — it’s predictable internet. Video calls that don’t stutter when someone starts a download. SSH that stays responsive during backups. Gaming that doesn’t spike during updates.

Test before and after. Measure latency under load. If it doesn’t improve, you haven’t identified the real bottleneck yet.