Skip to content

Queue Tree

Queue Tree is RouterOS’s advanced, hierarchical queuing mechanism. Built on the Hierarchical Token Bucket (HTB) algorithm, it lets you create multi-level bandwidth hierarchies: guarantee minimum rates to traffic classes, cap maximum rates, and ensure latency-sensitive traffic (VoIP, gaming) consistently wins over bulk transfers — all without per-IP rules.

Queue Tree organizes queues as a tree rooted at a system HTB (such as global or a physical interface). Parent queues act as rate governors; child queues subdivide that bandwidth by traffic class. A child that is not using its guaranteed rate lends unused capacity to siblings, so no bandwidth is ever stranded.

Queue Tree is the right tool when you need:

  • Hierarchical QoS with multiple bandwidth classes under a single uplink cap
  • Global traffic prioritization (VoIP above everything else)
  • Separate upload and download shaping without double-marking packets
  • More than one level of rate limits on the same traffic

For simpler needs — limiting a single IP or subnet — Simple Queues require less configuration.

FeatureQueue TreeSimple Queue
DirectionOne-directional (upload or download)Bidirectional (upload/download in one rule)
Traffic selectionPacket marks from /ip firewall mangleTarget IP/subnet address
OrderingNot ordered — all traffic passes togetherOrdered — rules evaluated top to bottom
HierarchyExplicit parent/child tree, any depthTwo levels (parent + children)
ComplexityHigher — requires mangle markingLower — self-contained
Best forComplex QoS policies, global prioritizationPer-host/subnet rate limiting

Note: Simple Queues can also use packet marks, but Queue Tree gives finer control over traffic direction and hierarchy depth.

Every queue in the tree has two rate parameters:

  • limit-at — the guaranteed (committed) rate. When the parent has spare capacity, the child is always given at least this rate.
  • max-limit — the ceiling. The child cannot exceed this rate even if the parent has idle capacity.

When a child queue is below its limit-at, it may borrow unused capacity from other siblings through the shared parent. When all children are competing for capacity, each is served down to its limit-at before any borrowing occurs.

Priority (1–8, where 1 is highest) controls which child reaches its max-limit first when bandwidth is contested. Priority has no effect on limit-at guarantees — those are always honored regardless of priority.

parent (max-limit=100M)
├── VoIP limit-at=2M max-limit=10M priority=1
├── Video limit-at=20M max-limit=60M priority=2
└── Bulk limit-at=5M max-limit=80M priority=7

In the example above: VoIP is always given at least 2 Mbps. When the link is idle, bulk traffic can use up to 80 Mbps. As the link fills, bulk is squeezed first (priority 7) while VoIP (priority 1) continues to reach its max-limit.

The parent parameter determines where in the packet flow the queue tree is attached.

Parent valueHTB attachmentUse case
globalPostrouting HTB — all outgoing trafficTotal upload cap across all interfaces
global-inPrerouting HTB — all incoming trafficTotal download cap
global-outAlias for globalSame as global
<interface>Interface-specific queuePer-interface upload shaping (e.g., ether1)
<queue-name>Child of another queue tree entryMulti-level hierarchy

Tip: Using interface names as the parent instead of global simplifies packet marking — upload traffic naturally reaches the WAN interface, and download reaches the LAN interface, so you only need one set of marks.

Queue Tree does not match traffic by IP address. It relies entirely on packet marks set in /ip firewall mangle. Traffic that carries no matching mark passes through the tree without entering any leaf queue.

The mangle chain you should use depends on the direction:

  • Download (traffic toward LAN) — use chain forward, mark in prerouting or forward
  • Upload (traffic toward WAN) — use chain forward, mark in forward
/ip firewall mangle
add chain=forward src-address=192.168.0.0/24 action=mark-packet \
new-packet-mark=upload-lan passthrough=yes
add chain=forward dst-address=192.168.0.0/24 action=mark-packet \
new-packet-mark=download-lan passthrough=yes

Important: Disable FastTrack (/ip firewall filter) or the connection tracking fast-path for traffic you want to queue. FastTracked packets bypass the mangle and queue subsystem entirely.


Example 1: Limit Specific Application Traffic

Section titled “Example 1: Limit Specific Application Traffic”

The simplest queue tree use case: cap a single traffic type (e.g., YouTube) to 5 Mbps without affecting other traffic.

Step 1 — Create a firewall address list:

/ip firewall address-list
add address=www.youtube.com list=YouTube

Step 2 — Mark matching packets in mangle:

/ip firewall mangle
add chain=forward dst-address-list=YouTube in-interface-list=LAN \
action=mark-packet new-packet-mark=pmark-youtube passthrough=yes

Step 3 — Create the queue tree rule:

/queue tree
add name=limit-youtube parent=global packet-mark=pmark-youtube max-limit=5M

Verify traffic is matched:

/queue tree print stats

A 100 Mbps uplink divided between three traffic classes with guaranteed minimums and a shared ceiling.

Scenario:

ClassGuaranteedMaximumPriority
VoIP2 Mbps10 Mbps1 (highest)
Interactive (web, SSH)20 Mbps90 Mbps3
Bulk (P2P, backups)5 Mbps80 Mbps7 (lowest)

Step 1 — Mark traffic in mangle:

/ip firewall mangle
# Mark VoIP (SIP + RTP)
add chain=forward protocol=udp dst-port=5060,5061,10000-20000 \
action=mark-packet new-packet-mark=voip passthrough=yes
# Mark interactive web and management
add chain=forward protocol=tcp dst-port=80,443,22 \
action=mark-packet new-packet-mark=interactive passthrough=yes
# Mark remaining traffic as bulk
add chain=forward action=mark-packet new-packet-mark=bulk passthrough=yes

Step 2 — Create the parent queue (uplink cap):

/queue tree
add name=uplink parent=global max-limit=100M

Step 3 — Add child queues under the parent:

/queue tree
add name=voip parent=uplink packet-mark=voip limit-at=2M max-limit=10M priority=1
add name=interact parent=uplink packet-mark=interactive limit-at=20M max-limit=90M priority=3
add name=bulk parent=uplink packet-mark=bulk limit-at=5M max-limit=80M priority=7

At 100 Mbps utilization, VoIP and interactive are served to their limit-at first. Remaining capacity is distributed to bulk. If VoIP is idle, its 2 Mbps is lent to higher-priority siblings before reaching bulk.


Example 3: Per-Interface Upload and Download Shaping

Section titled “Example 3: Per-Interface Upload and Download Shaping”

Using interface names as parents avoids the need to mark upload and download separately — each interface only carries one direction of traffic.

Scenario: 100 Mbps WAN uplink (ether1), 1 Gbps LAN (ether2). Shape upload to 95 Mbps and download to 100 Mbps with a VoIP priority lane in each direction.

Step 1 — Mark VoIP in both directions:

/ip firewall mangle
# Outgoing VoIP (upload)
add chain=forward out-interface=ether1 protocol=udp dst-port=5060,10000-20000 \
action=mark-packet new-packet-mark=voip-up passthrough=yes
# Incoming VoIP (download)
add chain=forward in-interface=ether1 protocol=udp src-port=5060,10000-20000 \
action=mark-packet new-packet-mark=voip-down passthrough=yes

Step 2 — Upload tree (attached to WAN interface):

/queue tree
add name=upload-total parent=ether1 max-limit=95M
add name=upload-voip parent=upload-total packet-mark=voip-up limit-at=2M max-limit=10M priority=1
add name=upload-data parent=upload-total limit-at=5M max-limit=93M priority=5

Step 3 — Download tree (attached to LAN interface):

/queue tree
add name=download-total parent=ether2 max-limit=100M
add name=download-voip parent=download-total packet-mark=voip-down limit-at=2M max-limit=10M priority=1
add name=download-data parent=download-total limit-at=5M max-limit=98M priority=5

Note: The upload-data and download-data queues have no packet-mark, so they catch all traffic not matched by the VoIP child queue. This acts as a default catch-all class.


Burst allows a queue to temporarily exceed its limit-at or max-limit to handle short traffic spikes (page loads, connection setup) without affecting steady-state traffic shaping.

ParameterDescription
burst-limitMaximum rate during burst
burst-thresholdBurst activates while average rate is below this value; deactivates when average exceeds it
burst-timeAveraging window used to calculate the current average rate (in seconds). Not the duration of the burst

How burst works:

  1. RouterOS measures the average rate over the burst-time window.
  2. If the average is below burst-threshold, burst is active — traffic can reach burst-limit.
  3. As soon as the average hits burst-threshold, burst deactivates and the queue drops to max-limit.

Rule of thumb: Set burst-threshold between limit-at and max-limit.

/queue tree
add name=web-burst parent=uplink packet-mark=interactive \
limit-at=20M max-limit=50M \
burst-limit=80M burst-threshold=35M burst-time=8s

In this example, web traffic can burst to 80 Mbps when the 8-second average is below 35 Mbps. Once sustained traffic pushes the average over 35 Mbps, the queue caps at 50 Mbps.


View all queue trees with live statistics:

/queue tree print stats

Sample output:

Flags: X - disabled, I - invalid
0 name="uplink" parent=global rate=87.3Mbps packet-rate=12345 bytes=1234567890
packets=9876543 queued-bytes=0 queued-packets=0 dropped=12
1 name="voip" parent=uplink rate=1.2Mbps packet-rate=120 bytes=123456789
packets=987654 queued-bytes=0 queued-packets=0 dropped=0 borrows=0 lends=450

Key statistics fields:

FieldDescription
rateCurrent throughput through the queue
packet-ratePackets per second
queued-bytes / queued-packetsBytes/packets currently buffered
droppedPackets dropped due to queue overflow
borrowsPackets forwarded above limit-at (borrowed capacity)
lendsUnused capacity lent to other queues

Watch a queue in real time:

/queue tree print stats interval=1

Reset counters:

/queue tree reset-counters-all

  • Verify packet marks exist: /ip firewall mangle print stats — the mangle rule must show increasing byte/packet counts.
  • Check FastTrack: tracked connections bypass queuing. Disable FastTrack for traffic you want to shape.
  • Confirm parent is set correctly. A queue with parent=global only shapes postrouting traffic; download traffic may require parent=global-in or a LAN interface.
  • The packet-mark on the queue tree entry does not match any marked packets.
  • The mangle rule is disabled, or the chain (forward, prerouting) is wrong for this traffic direction.
  • Run /ip firewall mangle print stats to confirm mark counts are incrementing.
  • Priority only applies between sibling queues (children of the same parent). It has no effect on parent queues.
  • Queues with limit-at=0 may not exhibit expected priority behavior under load — set a non-zero guaranteed rate on priority queues.
  • FastTrack is enabled for the connection — see above.
  • The queue entry is marked I (invalid) in print stats. An invalid queue is bypassed; check the parent name is spelled correctly.
  • Hardware offloading on the interface bypasses software queuing. Disable offload for shaped interfaces if present.

ParameterTypeDefaultDescription
namestringUnique name; can be used as parent value in other entries
parentstringAttachment point: global, global-in, interface name, or another queue name
packet-markstring""Packet mark from /ip firewall mangle to match; empty catches unmatched traffic
limit-atrate0Guaranteed (committed) rate; 0 = no guarantee
max-limitrate0Maximum rate ceiling; 0 = unlimited
priority1–88Service priority among siblings; 1 = highest, 8 = lowest
queuequeue typedefaultInner queue discipline (e.g., pcq-upload-default, sfq)
burst-limitrate0Peak burst rate; 0 = no burst
burst-thresholdrate0Burst on/off switch threshold
burst-timeduration0sRate averaging window for burst calculation
disabledbooleannoDisable without removing
commentstring""Free-text annotation
FieldDescription
rateCurrent throughput
packet-rateCurrent packets per second
bytesTotal bytes processed
packetsTotal packets processed
queued-bytesBytes currently in the buffer
queued-packetsPackets currently in the buffer
droppedTotal packets dropped
borrowsPackets forwarded above limit-at using borrowed capacity
lendsPackets that represented lent capacity to other queues