ONTAP QoS policy groups: ceilings, floors, and adaptive policy groups
NetApp ONTAP's storage Quality of Service (QoS) lets you cap noisy tenants, size performance to consumption with IOPS-per-TB policy groups, and (on current all-flash platforms) reserve a minimum share of throughput for latency-sensitive workloads. This guide covers every knob a storage administrator actually touches — fixed ceilings, adaptive policy groups, floors, assignment, monitoring counters, and the failure modes that trip people up.
Why QoS exists in ONTAP
A single runaway backup server scanning millions of files can starve an OLTP database sharing the same controller — even though both sit on healthy SSD aggregates. ONTAP solved this class of "noisy neighbor" problem first through System Manager controls and later (ONTAP 9.3 onward) fully at the cluster level with qos commands. Three distinct tools live under one framework:
- Fixed policy groups — a hard upper bound (
-max-throughput) on IOPS, throughput (MB/s or GB/s), or both. - Adaptive policy groups (ONTAP 9.5+) — capacity-proportional limits expressed as IOPS per terabyte, evaluated against consumed space rather than a static number you re-adjust after every resize.
- Floors — a guaranteed minimum throughput share for workloads whose latency must be defended against other tenants on shared media.
All three attach to the same primitive: the workload. Everything else is bookkeeping.
Workloads and policy groups: the model
- A workload is the QoS-visible unit of storage: typically a FlexVol volume, FlexGroup constituents, NVMe namespace, or LUN. QoS measures its IO where it lands on the node.
- A policy group is a named object holding the limits. By default one policy group is enforced individually per attached workload; sharing one group across many volumes then acts as separate budgets, unless you create it as a shared budget (see below) so the limit covers all members combined.
- Scope: classic groups are created against a single SVM; ONTAP 9.7+ supports
-cluster-wide trueobjects that any SVM can reference — handy for uniform desktop or branch-office profiles. - Default groups: untagged workloads land in the implicit
defaultpolicies, which set only a very high system ceiling — not an admission ticket, just a safety catch. - Background housekeeping (SnapMirror transfers, dedupe/scanner activity, tape NDMP) also appears as workloads such as those tagged
Workload Type: background; ceilings can be applied to protect foreground traffic during replication windows.
QoS is enforced in software on each node's scheduler ahead of WAFL, so it costs negligible CPU, needs no special hardware, and works uniformly on AFF, ASA, FAS with SSD, and hybrid systems. What it cannot do is make slow media faster — a floor reserves relative priority, not raw speed beyond what the hardware delivers.
Fixed policy groups (max-throughput ceilings)
The bread-and-butter control. Limit by IOPS, bytes per second, or both together; when both are set, whichever threshold hits first throttles.
# Create a ceiling per-SVM: 5,000 IOPS AND 600 MB/s max
cluster::> qos policy-group create -policy-group pg_oltp_cap \
-vserver svm_sales -max-throughput 5000iops,600MB/s
# Cluster-wide profile usable by every SVM (9.7+)
cluster::> qos policy-group create -policy-group pg_branch \
-cluster-wide true -max-throughput 1500iops
Unit grammar is consistent everywhere on the platform: iops, b/s, kb/s, mb/s, gb/s. Prefixes behave as k=1000. Inspect what you have, including which workloads currently consume a group:
qos policy-group show
qos policy-group show -policy-group pg_oltp_cap -instance
qos workload show # every workload + its effective group/limits
| Limit style | Syntax example | Effect |
|---|---|---|
| IOPS only | 8000iops | Caps operations; good guardrails for VM sprawl |
| Throughput only | 250MB/s | Caps large-block scans/restores without touching small-IOPS apps |
| Both | 5000iops,400MB/s | First-to-hit governs — the usual production choice |
Adaptive QoS (IOPS per TB)
Static ceilings age badly: a volume doubles, and yesterday's sensible limit becomes today's outage. Adaptive policy groups (introduced in ONTAP 9.5) express intensity instead — a linear ramp from expected to peak IOPS allocated per terabyte of consumed blocks.
# OLTP profile: expect 2,000 IOPS/TB, allow bursting to 4,000 IOPS/TB,
# never below 750 IOPS no matter how small the volume gets
cluster::> qos adaptive-policy-group create -policy-group apg_oltp \
-vserver svm_sales -expected-iops 2000/TB -peak-iops 4000/TB \
-absolute-minimum-iops 750 -block-size 32TB
# Attach while creating/updating a volume
cluster::> vol modify -vserver svm_sales -volume db01 \
-qos-adaptive-policy-group apg_oltp
-block-sizequantises the math: limits re-evaluate in block-size steps, so with 32TB blocks every 32TB consumed buys another chunk of expected IOPS.- Used-space gotcha: evaluation follows consumed capacity. Thin-provisioned volumes freshly migrated but mostly free start under-limited until they fill — deliberately pick a smaller block size (or fall back to a fixed group) if you need day-one guarantees.
-absolute-minimum-iopsis the floor beneath the ramp; ONTAP will not cut a workload below it even at near-zero utilization.- Adaptive groups cannot be edited after creation in early releases — treat them as versioned (create
apg_oltp_v2, migrate, delete the old one).
Min-throughput floors
Recent ONTAP releases (9.13–9.16 wave, AFF/ASA platforms — verify availability on your specific version before designing around it) flipped QoS from defensive to contractual: -min-throughput reserves capacity inside the scheduler for designated workloads.
# Guarantee the EHR database at least 12000iops even during restore storms
cluster::> qos policy-group create -policy-group pg_ehr_floor \
-vserver svm_clinical -min-throughput 12000iops
# Ceiling + floor combined in one contract
cluster::> qos policy-group modify -policy-group pg_ehr_floor \
-max-throughput 25000iops
Floors only make sense when total reserved bandwidth stays under what the nodes can actually deliver; oversubscribed floors degrade to "everyone fights politely," so sum your reservations and compare them with measured peak service rates before going live. On systems without floor support the flag is rejected at create time — which is your quickest compatibility probe.
Assignment runbook (volumes, LUNs, namespaces)
# Attach / detach / switch groups
vol modify -vserver svm1 -volume vol_homedirs -qos-policy-group pg_files
vol modify -vserver svm1 -volume vol_homedirs -qos-policy-group "" # detach
vol modify -vserver svm1 -lun lun_oracle -qos-policy-group pg_oltp_cap
# Review mapping and remove an empty group
qos workload show -workload-volume vol_homedirs -instance
qos policy-group delete -policy-group pg_old -vserver svm1
- Ducking out of a group requires setting the attribute to empty string — you cannot simply omit it on modify.
- Group deletion fails while any workload still references it; the error names the offender, which doubles as a handy audit command.
- Moving volumes between groups is non-disruptive; enforcement changes take effect immediately, though per-workload statistics history resets under the new identity.
- FlexGroup constituents inherit assignments individually (each constituent counts as its own workload) — ceilings therefore multiply across constituents; prefer cluster-wide defaults or generous totals there.
Monitoring & counters
# Live IOPS/throughput for assigned workloads
qos statistics volume performance show -sort-by iops
# Latency attribution incl. the QoS wait bucket
qos statistics volume latency show
# Node-level view: system workloads, adaptive utilization, ceilings reached
qos statistics workload resource utilization show
When a ceiling binds, the throttle component lights up in the latency breakdown while disk time stays comfortable — that split is the signature difference between "policy cap" and "media congestion." Correlate with host-side observations before concluding; see our performance troubleshooting QoS section for the step-by-step drill-down and the ONTAP-wide latency anatomy.
Troubleshooting matrix
| Symptom | Check | Fix |
|---|---|---|
| Workload slower than expected, disks idle | qos workload show -instance — unexpected group attached? | Detach/raise the limit: vol modify ... -qos-policy-group ... |
| Adaptive volume throttled "too early" | Consumed TB small + large block-size ⇒ low computed budget | Shrink block-size, raise absolute-minimum-iops, or pin a fixed group |
| Can't delete policy group | Workload still assigned | Clear assignment first; error output lists the volume |
| Floor create fails | Platform/release lacks floor support, or reservation oversubscribes node capability | Verify version/platform support; lower reservations |
| Shared budget eating itself | Members count into one aggregate ceiling | Split members into scoped groups sized for the sum |
| Replication window slows prod | SnapMirror background workload uncapped | Put a modest ceiling on the background workload |
Production checklist
- Name groups by contract, not hardware:
pg_silver_4kiopsbeatspg_test2in audits. - Set ceilings only where contention is real — needless caps add latency without saving capacity.
- Prefer the pair
IOPS + MB/sover either alone; they fail differently and complement each other. - For scaling estates, default new volumes onto adaptive groups and let exceptions get fixed ones.
- Re-validate assignments after storage migrations/reorgs — orphaned stale caps are the top recurring finding.
- Chart
qos statistics volume latency showmonthly; watch the throttle bucket drift.