Skip to main content
RunBook Academy

KubernetesCVIII · ResourceQuotaResourceQuota

Quota scoping — PriorityClass and StorageClass-based quotas

Advanced⏱ ~16 minkubectl

What you'll learn

  • Use PriorityClass-scoped quotas
  • Use StorageClass-scoped quotas
  • Reason about the use cases for scoping
  • Apply the operational discipline of scoping quotas appropriately

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

Quota scoping limits a quota to specific scopes. This lesson walks PriorityClass-scoped quotas, StorageClass-scoped quotas, the use cases, and the operational discipline.

PriorityClass-scoped quotas

apiVersion: v1
kind: ResourceQuota
metadata:
  name: high-priority-quota
  namespace: tenant-a-prod
spec:
  hard:
    requests.cpu: "16"
    requests.memory: 32Gi
  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["high-priority"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: low-priority-quota
  namespace: tenant-a-prod
spec:
  hard:
    requests.cpu: "16"
    requests.memory: 32Gi
  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["low-priority"]

The PriorityClass-scoped quota only counts Pods with the specified PriorityClass. High-priority workloads get a separate budget from low-priority workloads.

The use case

flowchart LR
    A["Namespace: tenant-a-prod"] --> B["High-priority workload: 16 cores"]
    A --> C["Low-priority workload: 16 cores"]
    B --> D["PriorityClass: high-priority"]
    C --> E["PriorityClass: low-priority"]
    D --> F["Quota: high-priority-quota"]
    E --> G["Quota: low-priority-quota"]

The use case: a tenant has critical workloads (e.g., the checkout API) and batch workloads (e.g., analytics). The two should not compete for the same quota. PriorityClass-scoped quotas give each a separate budget.

StorageClass-scoped quotas

apiVersion: v1
kind: ResourceQuota
metadata:
  name: premium-storage-quota
  namespace: tenant-a-prod
spec:
  hard:
    requests.storage: 500Gi
    persistentvolumeclaims: "20"
  scopeSelector:
    matchExpressions:
      - scopeName: StorageClass
        operator: In
        values: ["premium-ssd"]
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: standard-storage-quota
  namespace: tenant-a-prod
spec:
  hard:
    requests.storage: 1Ti
    persistentvolumeclaims: "30"
  scopeSelector:
    matchExpressions:
      - scopeName: StorageClass
        operator: In
        values: ["standard-hdd"]

The StorageClass-scoped quota only counts PVCs with the specified StorageClass. Premium SSD storage gets a separate budget from standard HDD storage.

The use case

flowchart LR
    A["Namespace: tenant-a-prod"] --> B["Premium SSD: 500Gi"]
    A --> C["Standard HDD: 1Ti"]
    B --> D["StorageClass: premium-ssd"]
    C --> E["StorageClass: standard-hdd"]
    D --> F["Quota: premium-storage-quota"]
    E --> G["Quota: standard-storage-quota"]

The use case: a tenant uses premium SSD for production databases and standard HDD for non-critical workloads. Premium SSD is more expensive; a separate quota prevents overconsumption.

The scoping syntax

spec:
  scopeSelector:
    matchExpressions:
      - scopeName: PriorityClass
        operator: In
        values: ["high-priority"]
      - scopeName: StorageClass
        operator: In
        values: ["premium-ssd"]

Multiple scopes can be combined: a quota that counts only Pods with the specified PriorityClass AND only PVCs with the specified StorageClass.

Unscoped vs scoped quotas

flowchart LR
    A[Unscoped quota] --> B["Counts all Pods/PVCs in namespace"]
    C[Scoped quota] --> D["Counts only matching Pods/PVCs"]

The choice:

  • Unscoped. Counts every Pod/PVC in the namespace. Simpler; appropriate when all workloads have the same priority and storage tier.
  • Scoped. Counts only matching Pods/PVCs. More granular; appropriate when different workloads need separate budgets.

Quiz

Knowledge check · 4 questions

  1. Q1. What does a `PriorityClass`-scoped ResourceQuota let you express?

  2. Q2. A namespace may have several ResourceQuota objects with different scopes.

  3. Q3. Two scoped quotas cover a namespace, yet its total consumption is far above what either allows; find the gap.

    `ml-prod` has two ResourceQuotas: `high-quota` scoped to PriorityClass `high-priority` and `low-quota` scoped to PriorityClass `low-priority`, each with `requests.cpu: 16`. `kubectl describe resourcequota -n ml-prod` shows 11 of 16 and 9 of 16. Summing the actual Pod requests in the namespace gives 58 cores, and this namespace is exhausting node allocatable.

  4. Q4. Name three `scopeName` values a ResourceQuota `scopeSelector` accepts besides `PriorityClass`, and say what a quota scoped to `BestEffort` counts.

Passing score: 75%. Answers are checked in this browser.

The operational discipline

Quota scoping in production rests on five non-negotiable elements:

  • Define PriorityClasses. Before scoping quotas, define the PriorityClasses in the cluster.
  • Define StorageClasses. Before scoping storage quotas, define the StorageClasses.
  • Use scoping when justified. Different budgets for different workloads.
  • Monitor scoped and unscoped separately. Each quota has its own utilisation.
  • Document the scoping. The runbook lists which quotas are scoped and why.

Quota scoping is a precision tool. Use it when different workloads need separate budgets; do not over-scope when a single unscoped quota suffices.