Configure advanced iptables QoS with DSCP marking and traffic classification

Advanced 35 min Apr 07, 2026 1,085 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up Quality of Service (QoS) on Linux servers using iptables DSCP marking, HTB queuing, and traffic classification. This tutorial shows you how to prioritize network traffic, implement bandwidth limits, and ensure critical applications get the network resources they need for optimal performance.

Prerequisites

  • Root or sudo access
  • Basic understanding of networking concepts
  • Active network interface
  • iptables installed and configured

What this solves

Network congestion can severely impact application performance, especially for latency-sensitive services like VoIP, video conferencing, and real-time databases. This tutorial configures advanced Quality of Service (QoS) using iptables DSCP marking and Hierarchical Token Bucket (HTB) queuing to prioritize critical traffic, implement bandwidth limits, and ensure fair resource allocation across your network infrastructure.

Step-by-step configuration

Install traffic control utilities

Install the iproute2 package which provides the tc (traffic control) command for managing network queues and traffic shaping.

sudo apt update
sudo apt install -y iproute2 iptables-persistent netfilter-persistent
sudo systemctl enable netfilter-persistent
sudo dnf update -y
sudo dnf install -y iproute tc iptables-services
sudo systemctl enable iptables

Create HTB queuing discipline

Set up the HTB root queue with a total bandwidth limit and create priority classes for different types of traffic.

# Remove existing queues
sudo tc qdisc del dev eth0 root 2>/dev/null || true

# Create root HTB queue with 100Mbps total bandwidth
sudo tc qdisc add dev eth0 root handle 1: htb default 30

# Create root class with total bandwidth
sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit

# High priority class (40% bandwidth, can burst to 80%)
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 40mbit ceil 80mbit prio 1

# Medium priority class (40% bandwidth, can burst to 60%)
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40mbit ceil 60mbit prio 2

# Low priority class (20% bandwidth, can burst to 40%)
sudo tc class add dev eth0 parent 1:1 classid 1:30 htb rate 20mbit ceil 40mbit prio 3

Add SFQ queuing to leaf classes

Attach Stochastic Fair Queuing (SFQ) to each class to ensure fair distribution among flows within the same priority level.

# Add SFQ to high priority class
sudo tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10

# Add SFQ to medium priority class
sudo tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10

# Add SFQ to low priority class
sudo tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

Configure iptables DSCP marking rules

Create iptables rules to mark different types of traffic with appropriate DSCP values for classification.

# Create DSCP marking chain
sudo iptables -t mangle -N QOS_MARKING

# Jump to marking chain for all traffic
sudo iptables -t mangle -A OUTPUT -j QOS_MARKING
sudo iptables -t mangle -A FORWARD -j QOS_MARKING

# Mark VoIP traffic (SIP, RTP) - Expedited Forwarding (EF)
sudo iptables -t mangle -A QOS_MARKING -p udp --dport 5060 -j DSCP --set-dscp-class EF
sudo iptables -t mangle -A QOS_MARKING -p udp --dport 10000:20000 -j DSCP --set-dscp-class EF

# Mark video traffic - Assured Forwarding 41 (AF41)
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 1935 -j DSCP --set-dscp-class AF41
sudo iptables -t mangle -A QOS_MARKING -p udp --dport 1935 -j DSCP --set-dscp-class AF41

# Mark database traffic - Assured Forwarding 31 (AF31)
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 3306 -j DSCP --set-dscp-class AF31
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 5432 -j DSCP --set-dscp-class AF31

# Mark web traffic - Assured Forwarding 21 (AF21)
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 80 -j DSCP --set-dscp-class AF21
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 443 -j DSCP --set-dscp-class AF21

# Mark SSH traffic - Assured Forwarding 22 (AF22)
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 22 -j DSCP --set-dscp-class AF22

# Mark bulk transfer traffic - Best Effort (default)
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 21 -j DSCP --set-dscp-class BE
sudo iptables -t mangle -A QOS_MARKING -p tcp --dport 20 -j DSCP --set-dscp-class BE

Create traffic classification filters

Set up tc filters to classify packets based on their DSCP markings and direct them to appropriate HTB classes.

# Filter for high priority traffic (EF, AF41)
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip tos 0xb8 0xfc flowid 1:10
sudo tc filter add dev eth0 parent 1: protocol ip prio 1 u32 match ip tos 0x88 0xfc flowid 1:10

# Filter for medium priority traffic (AF31, AF22)
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip tos 0x68 0xfc flowid 1:20
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip tos 0x50 0xfc flowid 1:20

# Filter for web traffic (AF21)
sudo tc filter add dev eth0 parent 1: protocol ip prio 2 u32 match ip tos 0x48 0xfc flowid 1:20

# Default filter for unmarked traffic (low priority)
sudo tc filter add dev eth0 parent 1: protocol ip prio 3 u32 match ip dst 0.0.0.0/0 flowid 1:30

Configure ingress traffic control

Set up ingress filtering to control incoming traffic rates and prevent network flooding.

# Add ingress qdisc
sudo tc qdisc add dev eth0 handle ffff: ingress

# Limit total ingress rate to 80Mbps
sudo tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match u32 0 0 police rate 80mbit burst 10k drop flowid :1

# Rate limit specific services
# Limit HTTP/HTTPS to 50Mbps
sudo tc filter add dev eth0 parent ffff: protocol ip prio 2 u32 match ip sport 80 0xffff police rate 25mbit burst 5k drop
sudo tc filter add dev eth0 parent ffff: protocol ip prio 2 u32 match ip sport 443 0xffff police rate 25mbit burst 5k drop

Create QoS monitoring script

Create a script to monitor QoS statistics and queue performance for ongoing optimization.

#!/bin/bash

echo "=== HTB Queue Statistics ==="
tc -s class show dev eth0

echo ""
echo "=== Filter Statistics ==="
tc -s filter show dev eth0

echo ""
echo "=== Ingress Statistics ==="
tc -s qdisc show dev eth0

echo ""
echo "=== DSCP Marking Rules ==="
iptables -t mangle -L QOS_MARKING -n -v

echo ""
echo "=== Network Interface Statistics ==="
cat /proc/net/dev | grep eth0

Make the monitoring script executable

Set proper permissions for the monitoring script and create a systemd timer for regular monitoring.

sudo chmod 755 /usr/local/bin/qos-monitor.sh

# Test the monitoring script
sudo /usr/local/bin/qos-monitor.sh

Save iptables rules permanently

Ensure iptables rules persist across system reboots by saving them to the appropriate configuration files.

sudo netfilter-persistent save
sudo systemctl enable netfilter-persistent
sudo iptables-save > /etc/sysconfig/iptables
sudo systemctl enable iptables

Create QoS startup script

Create a systemd service to automatically configure traffic control rules at system startup.

[Unit]
Description=QoS Traffic Control Setup
After=network.target
Wants=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/qos-setup.sh
ExecStop=/usr/local/bin/qos-teardown.sh

[Install]
WantedBy=multi-user.target

Create QoS setup and teardown scripts

Create scripts to set up and tear down the QoS configuration for easy management.

#!/bin/bash

# Interface to configure
INTERFACE="eth0"
TOTAL_BW="100mbit"

# Remove existing queues
tc qdisc del dev $INTERFACE root 2>/dev/null || true
tc qdisc del dev $INTERFACE ingress 2>/dev/null || true

# Create root HTB queue
tc qdisc add dev $INTERFACE root handle 1: htb default 30
tc class add dev $INTERFACE parent 1: classid 1:1 htb rate $TOTAL_BW

# Create priority classes
tc class add dev $INTERFACE parent 1:1 classid 1:10 htb rate 40mbit ceil 80mbit prio 1
tc class add dev $INTERFACE parent 1:1 classid 1:20 htb rate 40mbit ceil 60mbit prio 2
tc class add dev $INTERFACE parent 1:1 classid 1:30 htb rate 20mbit ceil 40mbit prio 3

# Add SFQ to leaf classes
tc qdisc add dev $INTERFACE parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev $INTERFACE parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev $INTERFACE parent 1:30 handle 30: sfq perturb 10

# Add filters
tc filter add dev $INTERFACE parent 1: protocol ip prio 1 u32 match ip tos 0xb8 0xfc flowid 1:10
tc filter add dev $INTERFACE parent 1: protocol ip prio 1 u32 match ip tos 0x88 0xfc flowid 1:10
tc filter add dev $INTERFACE parent 1: protocol ip prio 2 u32 match ip tos 0x68 0xfc flowid 1:20
tc filter add dev $INTERFACE parent 1: protocol ip prio 2 u32 match ip tos 0x50 0xfc flowid 1:20
tc filter add dev $INTERFACE parent 1: protocol ip prio 2 u32 match ip tos 0x48 0xfc flowid 1:20
tc filter add dev $INTERFACE parent 1: protocol ip prio 3 u32 match ip dst 0.0.0.0/0 flowid 1:30

# Configure ingress
tc qdisc add dev $INTERFACE handle ffff: ingress
tc filter add dev $INTERFACE parent ffff: protocol ip prio 1 u32 match u32 0 0 police rate 80mbit burst 10k drop flowid :1

echo "QoS configuration applied successfully"

Create the teardown script

Create a script to cleanly remove all QoS configurations when needed.

#!/bin/bash

INTERFACE="eth0"

# Remove all traffic control rules
tc qdisc del dev $INTERFACE root 2>/dev/null || true
tc qdisc del dev $INTERFACE ingress 2>/dev/null || true

echo "QoS configuration removed successfully"

Enable the QoS service

Make the scripts executable and enable the systemd service to start automatically.

sudo chmod +x /usr/local/bin/qos-setup.sh
sudo chmod +x /usr/local/bin/qos-teardown.sh

sudo systemctl enable qos-setup.service
sudo systemctl start qos-setup.service
sudo systemctl status qos-setup.service

Verify your setup

Test the QoS configuration to ensure traffic is being classified and shaped correctly.

# Check HTB classes and statistics
sudo tc -s class show dev eth0

# Check filter rules
sudo tc -s filter show dev eth0

# Verify iptables DSCP marking
sudo iptables -t mangle -L QOS_MARKING -n -v

# Test traffic classification with iperf3 (if available)
iperf3 -c example.com -p 80 -t 10

# Monitor real-time queue statistics
watch -n 1 'tc -s class show dev eth0'
Note: Replace eth0 with your actual network interface name. Use ip link show to list available interfaces.

Monitor and validate QoS performance

Set up continuous monitoring

Create a monitoring solution to track QoS performance and identify optimization opportunities.

# Create log directory for QoS metrics
sudo mkdir -p /var/log/qos

# Create monitoring cron job
echo "*/5 * * * * root /usr/local/bin/qos-monitor.sh >> /var/log/qos/qos-stats.log 2>&1" | sudo tee -a /etc/crontab

# Restart cron service
sudo systemctl restart cron || sudo systemctl restart crond

Configure DSCP validation script

Create a script to test and validate DSCP marking for different traffic types.

#!/bin/bash

# Test DSCP marking for different protocols
echo "Testing DSCP marking..."

# Simulate different traffic types and check DSCP values
echo "HTTP traffic should be marked AF21 (0x48):"
tcpdump -i eth0 -c 5 "tcp port 80" -v 2>/dev/null | grep "tos"

echo "SSH traffic should be marked AF22 (0x50):"
tcpdump -i eth0 -c 5 "tcp port 22" -v 2>/dev/null | grep "tos"

echo "Database traffic should be marked AF31 (0x68):"
tcpdump -i eth0 -c 5 "tcp port 3306 or tcp port 5432" -v 2>/dev/null | grep "tos"

Make the test script executable

Set appropriate permissions for the DSCP validation script.

sudo chmod +x /usr/local/bin/test-dscp.sh

Common issues

Symptom Cause Fix
QoS rules not applied after reboot Scripts not enabled in systemd sudo systemctl enable qos-setup.service
Traffic not being classified Wrong interface name in scripts Update INTERFACE variable with ip link show
DSCP marking not working iptables rules not saved sudo netfilter-persistent save
High latency despite QoS Buffer bloat or incorrect bandwidth limits Reduce HTB queue lengths and adjust bandwidth values
No statistics showing in tc output No traffic matching filters Check filter rules with tc -s filter show dev eth0
Permission denied when running scripts Scripts not executable sudo chmod +x /usr/local/bin/qos-*.sh
Important: QoS is most effective when applied at network bottlenecks. Configure QoS on routers and gateways where traffic aggregation occurs for maximum impact.

Next steps

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 04:08 · reachable in a message, no ticket form