Back to blog

MikroTik / RouterOS  ·  Tutorial  ·  May 2026

QoS for Hospitality
Networks on MikroTik

Stop one guest's Netflix from killing the PMS connection. Simple Queue for quick caps, Queue Tree for VLAN-aware priority, PCQ for fair per-client sharing, and DSCP marking that survives across the whole path.

RouterOS QoS Queue Tree Simple Queue PCQ DSCP Mangle Hospitality MTCNA+

WAN 100 Mbps Starlink RouterOS Queue Tree + Mangle DSCP marking Staff / MGMT 40 Mbps guaranteed · priority 1 Guest WiFi 50 Mbps shared PCQ · priority 3 IoT 10 Mbps hard cap · priority 8 PCQ splits guest pool fairly Guest 1: 8 Mbps Guest 2: 8 Mbps … (÷ active clients) Queue Tree shapes VLAN traffic. PCQ divides the guest pool fairly per client. Staff always gets priority.

In a hotel or villa with a shared internet connection, quality of service is not optional — it's the difference between a 5-star review and a 1-star complaint about WiFi. Without QoS, a single guest running a 4K stream will saturate the uplink and make the property management system unresponsive, VoIP calls choppy, and every other guest unhappy. RouterOS gives you the tools to prevent this: Simple Queue for quick per-client caps, Queue Tree for VLAN-aware hierarchical shaping, and PCQ for automatic fair-share distribution without having to touch a rule for every new device.

We build this in three layers. Start with Simple Queue — fast to deploy, sufficient for small properties. Then upgrade to Queue Tree with VLAN-based priority. Finally add DSCP marking so traffic classification survives across switches and APs, and PCQ so the guest pool divides itself automatically no matter how many clients connect.

Prerequisites

01

How RouterOS QoS Works

QoS in RouterOS is a pipeline: mark traffic in mangle, shape it in queues. You can't skip the marking step and expect queues to know which traffic is which.

RouterOS QoS works in two stages that must work together:

Stage 1 — Classification (Mangle): packets are inspected and marked in the prerouting or forward chain. Marks identify which VLAN, which service, or which priority class a packet belongs to. Without marks, queues can only act on everything equally.

Stage 2 — Shaping (Queues): marked packets enter a queue structure that enforces rate limits, priorities, and fair-share rules. Simple Queue works on source/destination addresses. Queue Tree works on interface traffic and packet marks from mangle.

Packet from LAN MANGLE prerouting chain set-mark=guest-traffic marked QUEUE TREE match packet-mark enforce rate / priority PCQ fair-share WAN shaped output DSCP set in mangle survives to APs Mangle classifies → Queue Tree shapes → DSCP carries the classification to downstream devices

The key insight: queues don't identify traffic, mangle does. A queue rule that matches src-address=192.168.30.0/24 works but doesn't scale — you'd need to update it every time the subnet changes. The correct pattern is: mangle marks packets from VLAN 30 with packet-mark=guest-traffic, and the queue matches on that mark. The mark is the contract between mangle and the queue.

⚠ Gotcha — Queue Direction Matters: Upload vs Download

In RouterOS, upload (LAN → WAN) is shaped on the outgoing WAN interface. Download (WAN → LAN) is shaped on the outgoing LAN interface — or with Simple Queue on the dst-address. Getting the interface direction wrong is the most common reason a queue appears configured but has no effect. Always verify with /queue print stats that the byte counters are incrementing.

02

Simple Queue — Quick Per-Client Caps

The fastest path to "no single guest can hog the connection." Two fields, one rule per target. Start here.

Simple Queue is the easiest QoS tool in RouterOS. It matches on source or destination address, applies a max-limit for upload and download, and optionally bursts above the limit for short transfers. No mangle required. Ideal for small properties (up to ~20 clients) or as a quick fix while you build the full Queue Tree.

Cap the Entire Guest Subnet

RouterOS — Simple Queue: cap guest VLAN total bandwidth
# Total guest pool: 50 Mbps down / 20 Mbps up
# All guests share this pool — one queue for the whole subnet
[admin@MikroTik] > /queue simple add \
    name="guest-pool" \
    target=192.168.30.0/24 \
    max-limit=50M/20M \
    comment="Guest WiFi VLAN 30 — total pool"
# max-limit=download/upload (note: download first, then upload)
# 50M/20M = 50 Mbps download, 20 Mbps upload ceiling for ALL guests combined.

Per-Client Cap with Burst

Add individual guest caps so one device can't monopolise the pool. Burst allows full speed for a short duration (good for page loads) then throttles to the sustained rate.

RouterOS — Simple Queue: per-client cap with burst
# Per-guest cap: 10 Mbps down / 5 Mbps up sustained
# Burst: 20 Mbps for the first 10 seconds (makes pages feel fast)
[admin@MikroTik] > /queue simple add \
    name="guest-per-client" \
    target=192.168.30.0/24 \
    max-limit=10M/5M \
    burst-limit=20M/10M \
    burst-threshold=8M/4M \
    burst-time=10s/10s \
    queue=pcq-download-default/pcq-upload-default \
    comment="Guest per-client PCQ cap"
# burst-limit: max speed during burst
# burst-threshold: once sustained rate exceeds this, burst stops
# burst-time: burst window in seconds
# queue=pcq-*: use PCQ for fair per-IP sharing within the rule

# Verify queue is active and counting bytes:
[admin@MikroTik] > /queue simple print stats
 NAME             RATE         PACKET-RATE  QUEUED-BYTES  DROPPED
 guest-pool       12.4 Mbps    1823         0 B           0
 guest-per-client 1.2 Mbps     183          0 B           0
# RATE shows current throughput. DROPPED > 0 means shaping is active.
# If RATE = 0 and you expect traffic: check interface direction.
Queues Simple Queues + Add
WinBox 4.1 — Queues › Simple Queues Interfaces Bridge IP Firewall Queues Simple Queues Queue Tree Queue Types + Add New Simple Queue — guest-per-client Name guest-per-client Target 192.168.30.0/24 Max Limit ↓ / ↑ 10M / 5M BURST Burst Limit ↓ / ↑ 20M / 10M Burst Time ↓ / ↑ 10s / 10s Queue Type ↓ / ↑ pcq-download-default ▾ pcq-upload-default ▾ Apply Queues › Simple Queues › + Add · set Target = guest subnet · PCQ queue type for automatic per-client fair-share
Queues › Simple Queues → + Add. Set Target to the guest subnet, Max Limit for the sustained rate, Burst Limit for the short-term peak. Queue Type = pcq-download-default / pcq-upload-default activates PCQ fair-sharing within this rule automatically.
⚠ Gotcha — Simple Queue Order Matters

RouterOS Simple Queues are processed top-to-bottom, first match wins — exactly like firewall rules. If you have a per-client queue (target = 192.168.30.5) and a pool queue (target = 192.168.30.0/24), the per-client rule must appear before the pool rule. A more-specific address match must always be above a less-specific one. Check order with /queue simple print and use place-before= when adding.

03

Mangle — Classify Traffic by VLAN

Before Queue Tree can prioritise by VLAN, mangle must mark each packet. This is the classification layer that makes everything else work.

Mangle runs in the prerouting chain and marks packets before they enter the queue. We create one packet-mark per traffic class. Queue Tree will match these marks and apply the correct rate limits and priorities.

RouterOS — mangle: classify traffic by VLAN
[admin@MikroTik] > /ip firewall mangle

# ── STAFF / MGMT — highest priority ──────────────────────────────────────
[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting \
    in-interface=vlan10 \
    action=mark-packet \
    new-packet-mark=staff-traffic \
    passthrough=no \
    comment="Mark Staff VLAN 10 traffic"

# ── GUEST — medium priority, PCQ fair-share ───────────────────────────────
[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting \
    in-interface=vlan30 \
    action=mark-packet \
    new-packet-mark=guest-traffic \
    passthrough=no \
    comment="Mark Guest VLAN 30 traffic"

# ── IOT — lowest priority, hard cap ──────────────────────────────────────
[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting \
    in-interface=vlan40 \
    action=mark-packet \
    new-packet-mark=iot-traffic \
    passthrough=no \
    comment="Mark IoT VLAN 40 traffic"

# ── DSCP marking — set alongside packet-mark ─────────────────────────────
# Staff gets DSCP EF (Expedited Forwarding = highest priority class)
# Guest gets DSCP AF21 (medium assured forwarding)
# IoT gets DSCP CS1 (lower than best-effort — "background")
[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting in-interface=vlan10 \
    action=change-dscp new-dscp=46 \
    passthrough=yes \
    comment="DSCP EF (46) for Staff"

[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting in-interface=vlan30 \
    action=change-dscp new-dscp=18 \
    passthrough=yes \
    comment="DSCP AF21 (18) for Guest"

[admin@MikroTik] /ip/firewall/mangle> add \
    chain=prerouting in-interface=vlan40 \
    action=change-dscp new-dscp=8 \
    passthrough=yes \
    comment="DSCP CS1 (8) for IoT"

# Verify marks are being applied (check byte counters after traffic flows):
[admin@MikroTik] > /ip firewall mangle print stats
 #  CHAIN       ACTION       BYTES        PACKETS  COMMENT
 0  prerouting  mark-packet  142.3 MiB    398241   Mark Staff VLAN 10
 1  prerouting  mark-packet  890.1 MiB    1204821  Mark Guest VLAN 30
 2  prerouting  mark-packet  12.4 MiB     84921    Mark IoT VLAN 40
# Zero bytes on a rule = no traffic from that VLAN or wrong interface name.
⚠ Gotcha — passthrough=no on mark-packet, passthrough=yes on change-dscp

The mark-packet rules use passthrough=no — once a packet is marked, stop processing further mangle rules for it. This prevents a staff packet from accidentally also matching a guest rule. The change-dscp rules use passthrough=yes — the DSCP change is applied and the packet continues, because the mark-packet rule already ran first (or runs after, depending on order). If you set passthrough=no on the DSCP rule, the packet-mark from the previous rule won't have been applied yet. Keep mark-packet first, DSCP second, both referencing the same in-interface.

04

Queue Tree — VLAN-Aware Priority Shaping

Queue Tree is hierarchical: a parent queue owns the total WAN bandwidth, child queues each claim a slice with a priority and a rate limit.

Queue Tree operates on interface traffic and matches packet marks set by mangle. The structure is always a parent with children. The parent defines the total pipe (your WAN uplink speed). Children define per-class limits and priorities. When the link is congested, higher-priority children get served first; lower-priority children only get bandwidth when higher ones don't need it.

PARENT: wan-shaper interface=ether1 (WAN out) max-limit=100M staff-queue packet-mark=staff-traffic max-limit=40M priority=1 (highest) guaranteed minimum guest-queue packet-mark=guest-traffic max-limit=50M priority=3 PCQ per-client fair-share iot-queue packet-mark=iot-traffic max-limit=10M priority=8 (lowest) hard cap, background Under congestion: Staff (priority 1) served first → Guest (priority 3) → IoT (priority 8) only gets what's left after Staff and Guest

Step 1 — Create PCQ Queue Types

PCQ (Per-Connection Queue) automatically divides a bandwidth pool equally among active clients. RouterOS ships with two default PCQ types (pcq-download-default and pcq-upload-default). For the guest queue we use these directly — each unique destination IP (download) or source IP (upload) gets an equal share of the pool automatically.

RouterOS — verify or create PCQ queue types
# Check existing queue types — PCQ types should already exist
[admin@MikroTik] > /queue type print
 0  name="default"               kind=fifo
 1  name="ethernet-default"      kind=pfifo
 2  name="pcq-upload-default"    kind=pcq  classifier=src-address
 3  name="pcq-download-default"  kind=pcq  classifier=dst-address
# pcq-upload-default:   divides upload bandwidth equally per source IP
# pcq-download-default: divides download bandwidth equally per dest IP
# classifier=src-address = split by who's sending (upload)
# classifier=dst-address = split by who's receiving (download)
# These defaults are sufficient. You can create custom ones if needed.

# Optional: create a custom PCQ with rate-per-client limit
[admin@MikroTik] > /queue type add \
    name="pcq-guest-down" \
    kind=pcq \
    pcq-classifier=dst-address \
    pcq-rate=10M \
    pcq-burst-rate=20M \
    pcq-burst-threshold=8M \
    pcq-burst-time=10s
# pcq-rate=10M: each individual client is capped at 10 Mbps download.
# The pool queue (50M) and per-client cap (10M) work together:
# 5 guests = 10M each = 50M total = pool saturated.
# 3 guests = pool unused bandwidth distributed equally.

Step 2 — Build the Queue Tree

RouterOS — Queue Tree: parent + VLAN children
[admin@MikroTik] > /queue tree

# ── PARENT: total WAN upload shaper ──────────────────────────────────────
# Attach to WAN outgoing interface. All children inherit this ceiling.
[admin@MikroTik] /queue/tree> add \
    name="wan-shaper" \
    parent=ether1 \
    max-limit=100M \
    comment="WAN upload shaper — 100 Mbps Starlink"

# ── CHILD 1: Staff — priority 1, 40 Mbps ceiling ─────────────────────────
[admin@MikroTik] /queue/tree> add \
    name="staff-queue" \
    parent=wan-shaper \
    packet-mark=staff-traffic \
    max-limit=40M \
    priority=1 \
    queue=default \
    comment="Staff VLAN 10 — highest priority"

# ── CHILD 2: Guest — priority 3, 50 Mbps, PCQ per-client ─────────────────
[admin@MikroTik] /queue/tree> add \
    name="guest-queue" \
    parent=wan-shaper \
    packet-mark=guest-traffic \
    max-limit=50M \
    priority=3 \
    queue=pcq-guest-down \
    comment="Guest VLAN 30 — PCQ fair-share per client"

# ── CHILD 3: IoT — priority 8, 10 Mbps hard cap ─────────────────────────
[admin@MikroTik] /queue/tree> add \
    name="iot-queue" \
    parent=wan-shaper \
    packet-mark=iot-traffic \
    max-limit=10M \
    priority=8 \
    queue=default \
    comment="IoT VLAN 40 — background, hard cap"

# Verify the tree structure:
[admin@MikroTik] > /queue tree print
 NAME          PARENT       PACKET-MARK     MAX-LIMIT  PRIORITY
 wan-shaper    ether1       —               100M       8
 staff-queue   wan-shaper   staff-traffic   40M        1
 guest-queue   wan-shaper   guest-traffic   50M        3
 iot-queue     wan-shaper   iot-traffic     10M        8
Queues Queue Tree + Add
WinBox 4.1 — Queues › Queue Tree Queues Simple Queues Queue Tree + Add NAME PARENT PACKET-MARK MAX-LIMIT PRIORITY RATE wan-shaper ether1 — 100M 8 88.2M ▸ staff-queue wan-shaper staff-traffic 40M 1 12.1M ▸ guest-queue wan-shaper guest-traffic 50M 3 71.4M ▸ iot-queue wan-shaper iot-traffic 10M 8 4.7M New Queue Tree Entry — guest-queue Name guest-queue Parent wan-shaper ▾ Packet Mark guest-traffic ▾ Max Limit 50M Priority 3 Queue Type pcq-guest-down ▾ Apply Queues › Queue Tree › + Add · Parent = wan-shaper · Packet Mark = from mangle · Priority 1 (staff) beats 3 (guest) beats 8 (IoT)
Queues › Queue Tree shows the hierarchy. Parent wan-shaper owns the full 100 Mbps. Children match their mangle packet-mark and enforce their rate + priority. Under congestion, priority 1 (staff) is always served before priority 3 (guest) and priority 8 (IoT).
⚠ Gotcha — Queue Tree Only Shapes Upload (LAN→WAN) by Default

Attaching the parent queue to parent=ether1 (WAN interface) only shapes traffic going out that interface — which is your upload. To shape download (WAN→LAN), you need a second parent queue attached to the LAN interface or bridge, or use the global queue parent. Many deployments only shape upload because that's the direction that gets congested first — but on Starlink where download is also contended, add a mirror tree on the LAN-facing interface.

✕ What Didn't Work — Queue Tree With No Mangle Marks

Building a Queue Tree without mangle marks results in queues that match nothing — byte counters stay at zero, no shaping occurs. RouterOS will not warn you. The queues look correct in /queue tree print but silently do nothing because no packets carry the expected mark. Always verify mangle is working first (/ip firewall mangle print stats) before debugging Queue Tree. If mangle byte counters are zero, the problem is the mangle rules — fix those before touching the queue structure.

05

DSCP — Classification That Survives the Whole Path

Packet marks disappear when a packet leaves the router. DSCP is written into the IP header itself — APs, switches, and the ISP can all read and act on it.

A RouterOS packet-mark only exists inside the router's memory — it's gone the moment the packet exits to the WAN or crosses to a downstream AP. DSCP (Differentiated Services Code Point) is a 6-bit field in the IP header that travels with the packet end-to-end. Set it once in mangle and every DSCP-aware device on the path can honour it.

IP Packet DSCP=46 EF RouterOS mangle sets DSCP in header change-dscp=46 Managed Switch reads DSCP prioritises queue EF = strict priority WiFi AP maps DSCP → WMM AC_VO highest WiFi priority Client DSCP travels in the IP header — set once at the router, honoured by every DSCP-aware device along the path

The DSCP values used in this tutorial map to standard per-hop behaviours:

DSCP reference — values used in this tutorial
# DSCP value  Class name      Decimal  Use case
# ─────────────────────────────────────────────────────────────────────────
# EF          Expedited       46       VoIP, PMS, staff traffic — strict priority
#             Forwarding                queued ahead of everything else
#
# AF21        Assured         18       Guest streaming — medium priority,
#             Forwarding 2,1           dropped before EF under congestion
#
# CS1         Class           8        IoT, background traffic — below
#             Selector 1               best-effort; dropped first under load
#
# BE          Best Effort     0        Unmarked traffic — default treatment

# Verify DSCP is being set (use torch to inspect live packets):
[admin@MikroTik] > /tool torch interface=ether1 ip-protocol=any
# Look for the DSCP column in torch output.
# Or capture with /tool packet-sniffer and inspect headers.

06

Verify and Monitor

Three commands tell you everything: queue stats show what's being shaped, torch shows live rates, and mangle stats confirm classification is working.

RouterOS — QoS monitoring commands
# Live queue stats — watch byte counts and dropped packets
[admin@MikroTik] > /queue tree print stats interval=2
 NAME          RATE      PACKET-RATE  QUEUED-BYTES  DROPPED   COMMENT
 wan-shaper    88.2 Mbps 12048        0 B           0
 staff-queue   12.1 Mbps 1820         0 B           0         Staff VLAN
 guest-queue   71.4 Mbps 9814         48.2 KiB      12483     Guest — shaping active
 iot-queue     4.7 Mbps  414          0 B           0         IoT
# DROPPED > 0 on guest-queue = shaping is active, excess dropped.
# DROPPED = 0 on all = either below limits or classification broken.
# interval=2 refreshes every 2 seconds — Ctrl+C to stop.

# Check mangle is classifying (bytes must be non-zero):
[admin@MikroTik] > /ip firewall mangle print stats
 0  prerouting  mark-packet  142.3 MiB    398241   Mark Staff VLAN 10
 1  prerouting  mark-packet  890.1 MiB    1204821  Mark Guest VLAN 30
 2  prerouting  mark-packet  12.4 MiB     84921    Mark IoT VLAN 40

# Live per-IP breakdown inside a queue (who's using what):
[admin@MikroTik] > /queue simple print stats
# For PCQ queues, see per-flow breakdown:
[admin@MikroTik] > /queue type print stats where name="pcq-guest-down"

# See live traffic per IP on the guest VLAN:
[admin@MikroTik] > /tool torch interface=vlan30 src-address=192.168.30.0/24
 SRC-ADDRESS      DST-ADDRESS     PROTO  TX         RX
 192.168.30.5     23.45.67.89     TCP    9.8 Mbps   1.2 Mbps  ← near cap
 192.168.30.12    203.0.113.50    TCP    9.9 Mbps   800 Kbps  ← near cap
 192.168.30.8     142.250.80.1    TCP    2.1 Mbps   200 Kbps
# Torch shows real-time — exactly who is using what. Press Q to exit.
🛡 Operator's View — QoS for Hospitality

Start with the guest pool cap, add per-client PCQ, and you've solved 90% of complaints. The most common issue in hospitality WiFi is one device consuming the entire uplink — a guest torrent client, a phone with auto-backup, or a 4K stream. A 50M guest pool with 10M per-client PCQ cap means no single guest can take more than 10M even if the rest of the pool is idle. This alone eliminates the "someone's hogging the WiFi" complaint category.

Staff and PMS traffic should always be on a separate VLAN with priority 1. Even if a guest pool is being saturated, staff queues at priority 1 will preempt guest and IoT traffic. This means check-in terminals, payment processing, and the property management system always have bandwidth — even during full occupancy peak hours.

Monitor during peak hours, not at 03:00. QoS issues appear under load. Check /queue tree print stats interval=2 during dinner service (when guests are in rooms) or on a rainy afternoon. If guest DROPPED is non-zero and guest RATE equals max-limit, shaping is working. If staff-queue RATE is near its ceiling and guest-queue is also at max, you need a bigger WAN pipe — QoS cannot create bandwidth, only allocate what exists.

IoT hard cap protects the network from firmware updates and telemetry storms. IP cameras, smart thermostats, and access control systems sometimes push large firmware updates or cloud sync bursts at inopportune times. A 10 Mbps hard cap on the IoT VLAN ensures these background processes never affect guest or staff experience — and if an IoT device is compromised and starts flooding traffic, the cap limits the damage.

Takeaways

  1. Queues don't classify — mangle does. Build mangle packet-marks first, verify the byte counters are non-zero, then build the Queue Tree. Zero-byte mangle rules mean your queues match nothing and silently do nothing.
  2. Simple Queue is sufficient for small properties and quick deployments. Use it to cap the guest subnet and apply PCQ for per-client fairness. You can add Queue Tree later without removing Simple Queues — they operate in parallel.
  3. Queue Tree parent attaches to the WAN interface and shapes upload only by default. To shape download as well, add a second parent on the LAN-facing interface or bridge. On Starlink where download is also contested, both directions matter.
  4. PCQ is the correct tool for guest WiFi — never add per-IP Simple Queues manually for guests. PCQ automatically divides the pool equally among however many clients are active. Manual per-IP queues don't scale (guests come and go), and they require constant maintenance.
  5. Priority in Queue Tree is 1 (highest) to 8 (lowest) — the opposite of what most people expect. Staff = 1, Guest = 3, IoT = 8. Getting this backwards means IoT gets priority over staff — exactly the wrong outcome.
  6. DSCP survives the router boundary — packet-marks don't. If you have DSCP-aware APs (Ubiquiti UniFi, for example), set DSCP in mangle and the APs will honour it via WMM. Staff VoIP and PMS traffic marked EF (46) gets the highest WiFi access category automatically.
  7. Verify with /queue tree print stats interval=2 under real load. DROPPED > 0 on the guest queue confirms shaping is active. Zero everywhere with traffic flowing means classification is broken — go back to mangle stats first.

Running a hotel, villa, or SMB in Crete?

NOCTIS configures QoS for hospitality networks — VLAN-aware bandwidth allocation, PCQ guest fairness, and DSCP marking that works end-to-end across your APs. If your guests are complaining about WiFi speed while staff can't access the PMS, book a call to fix the root cause.

Book a Discovery Call →