Set up Kubernetes resource quotas and limit ranges to control CPU, memory, and storage consumption at the namespace level. This tutorial covers implementing resource constraints, monitoring usage, and troubleshooting quota issues for multi-tenant cluster management.
Prerequisites
- Running Kubernetes cluster with kubectl access
- Cluster admin permissions for creating quotas and limit ranges
What this solves
Kubernetes resource quotas and limit ranges prevent runaway applications from consuming all cluster resources and ensure fair resource allocation across namespaces. Resource quotas set hard limits on total resource consumption per namespace, while limit ranges define minimum and maximum resource constraints for individual pods and containers.
Understanding resource quotas and limit ranges
Resource quotas control the total amount of compute resources (CPU, memory) and object counts (pods, services, persistent volumes) that can be consumed in a namespace. They act as a budget that prevents any single namespace from monopolizing cluster resources.
Limit ranges complement quotas by setting constraints on individual objects within a namespace. They define minimum and maximum CPU and memory limits for containers, ensuring consistent resource allocation policies across all workloads.
Step-by-step configuration
Create a test namespace
Start by creating a dedicated namespace to demonstrate resource management policies.
kubectl create namespace production-app
kubectl get namespaces
Configure namespace resource quotas
Create a resource quota that limits total CPU, memory, and object counts for the namespace.
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production-app
spec:
hard:
# Compute resource limits
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
# Object count limits
pods: "10"
persistentvolumeclaims: "5"
services: "3"
secrets: "10"
configmaps: "10"
# Storage limits
requests.storage: "50Gi"
Apply the resource quota to your namespace.
kubectl apply -f resource-quota.yaml
kubectl describe quota production-quota -n production-app
Implement limit ranges for pods and containers
Create a limit range to enforce minimum and maximum resource constraints on individual containers and pods.
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: production-app
spec:
limits:
# Container-level limits
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
min:
cpu: "50m"
memory: "64Mi"
max:
cpu: "2"
memory: "4Gi"
# Pod-level limits
- type: Pod
min:
cpu: "100m"
memory: "128Mi"
max:
cpu: "4"
memory: "8Gi"
# Persistent volume limits
- type: PersistentVolumeClaim
min:
storage: "1Gi"
max:
storage: "20Gi"
Apply the limit range configuration.
kubectl apply -f limit-range.yaml
kubectl describe limitrange production-limits -n production-app
Test resource quota enforcement
Deploy a test application to verify that resource quotas and limits are properly enforced.
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
namespace: production-app
spec:
replicas: 2
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
ports:
- containerPort: 80
Deploy the test application and check resource allocation.
kubectl apply -f test-deployment.yaml
kubectl get pods -n production-app
kubectl describe quota production-quota -n production-app
Create a resource quota for multiple resource types
Configure a comprehensive quota that includes compute resources, storage, and Kubernetes objects.
apiVersion: v1
kind: ResourceQuota
metadata:
name: comprehensive-quota
namespace: production-app
spec:
hard:
# Compute quotas
requests.cpu: "6"
requests.memory: "12Gi"
limits.cpu: "12"
limits.memory: "24Gi"
# Object quotas
count/pods: "20"
count/services: "5"
count/secrets: "15"
count/configmaps: "15"
count/persistentvolumeclaims: "8"
count/deployments.apps: "10"
count/replicasets.apps: "20"
# Storage quotas
requests.storage: "100Gi"
persistentvolumeclaims: "10"
Replace the existing quota with the comprehensive version.
kubectl delete resourcequota production-quota -n production-app
kubectl apply -f comprehensive-quota.yaml
kubectl get resourcequota -n production-app
Configure priority class quotas
Create quotas based on priority classes to ensure critical workloads get guaranteed resources.
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: production-app
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
pods: "5"
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["high-priority"]
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "High priority class for critical applications"
Apply the priority class and associated quota.
kubectl apply -f priority-quota.yaml
kubectl get priorityclass
kubectl get resourcequota -n production-app
Monitoring and troubleshooting resource constraints
Monitor quota usage
Check current resource consumption against quotas to identify potential issues before they occur.
# View detailed quota usage
kubectl describe resourcequota -n production-app
Check quota status across all namespaces
kubectl get resourcequota --all-namespaces
Monitor limit range enforcement
kubectl describe limitrange -n production-app
View resource consumption by pods
Analyze actual resource usage to optimize quota allocation and identify resource-hungry applications.
# Check resource requests and limits for all pods
kubectl top pods -n production-app
View detailed resource allocation
kubectl describe pods -n production-app | grep -E "Requests|Limits"
Check pod resource specifications
kubectl get pods -n production-app -o jsonpath='{range .items[]}{.metadata.name}{"\t"}{.spec.containers[].resources}{"\n"}{end}'
Test quota violation scenarios
Create deployments that exceed quota limits to understand error messages and troubleshooting approaches.
apiVersion: apps/v1
kind: Deployment
metadata:
name: resource-heavy-app
namespace: production-app
spec:
replicas: 5
selector:
matchLabels:
app: heavy-app
template:
metadata:
labels:
app: heavy-app
spec:
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "3"
memory: "6Gi"
Attempt to deploy the resource-heavy application and observe quota enforcement.
kubectl apply -f quota-violation-test.yaml
kubectl get events -n production-app --sort-by='.lastTimestamp'
kubectl describe replicaset -n production-app
Set up quota monitoring with kubectl
Create scripts to regularly monitor quota usage and alert when approaching limits.
#!/bin/bash
NAMESPACE="production-app"
THRESHOLD=80 # Alert when usage exceeds 80%
echo "Monitoring resource quotas for namespace: $NAMESPACE"
echo "=========================================="
Get quota information
kubectl get resourcequota -n $NAMESPACE -o custom-columns=NAME:.metadata.name,RESOURCE:.status.hard,USED:.status.used --no-headers | while read quota_name resources used; do
echo "Quota: $quota_name"
# Parse and compare CPU usage
if [[ $resources == "requests.cpu" ]]; then
cpu_limit=$(echo $resources | grep -o 'requests.cpu:[^,}]*' | cut -d':' -f2 | tr -d '"')
cpu_used=$(echo $used | grep -o 'requests.cpu:[^,}]*' | cut -d':' -f2 | tr -d '"')
if [[ -n $cpu_limit && -n $cpu_used ]]; then
echo " CPU: $cpu_used / $cpu_limit"
fi
fi
echo " Full status:"
kubectl describe resourcequota $quota_name -n $NAMESPACE | grep -A 10 "Resource.Used.Hard"
echo ""
done
Make the script executable and run it to monitor quota usage.
chmod +x quota-monitor.sh
./quota-monitor.sh
Advanced resource management configurations
For production environments, you may want to implement more sophisticated resource management policies. Learn about implementing Kubernetes resource quotas and limits for advanced workload isolation strategies.
Network policies work alongside resource quotas to provide comprehensive namespace isolation. Check out our guide on configuring Kubernetes network policies with Calico CNI for complete security and resource management.
Verify your setup
Confirm that your resource quotas and limit ranges are working correctly with these verification commands.
# Check all quotas in the namespace
kubectl get resourcequota -n production-app
Verify limit ranges are applied
kubectl get limitrange -n production-app
View quota usage details
kubectl describe resourcequota comprehensive-quota -n production-app
Check that pods are running with proper resource constraints
kubectl get pods -n production-app -o jsonpath='{range .items[]}{.metadata.name}{"\t"}{.spec.containers[].resources.requests}{"\n"}{end}'
Test quota enforcement by trying to exceed limits
kubectl run test-pod --image=nginx --requests='cpu=100m,memory=128Mi' --limits='cpu=6,memory=8Gi' -n production-app --dry-run=server
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Pods stuck in Pending state | Resource quota exceeded or insufficient resources | Check quota usage with kubectl describe quota -n namespace and adjust limits or scale down workloads |
| "exceeded quota" errors during deployment | Deployment requests exceed available quota | Either increase quota limits or reduce resource requests in pod specifications |
| Containers using more resources than expected | No resource limits set on containers | Apply limit ranges to enforce default limits or specify limits in pod specs |
| Quota shows incorrect usage after pod deletion | Finalizers preventing proper cleanup | Check for stuck pods with kubectl get pods -n namespace and force delete if necessary |
| LimitRange not applying to new pods | Pods created before LimitRange was applied | Restart deployments to pick up new limit ranges: kubectl rollout restart deployment -n namespace |
| Unable to create PVCs due to storage quota | Storage requests exceed quota limits | Increase storage quota or use smaller persistent volumes |
Next steps
- Configure Kubernetes Pod Security Standards with admission controllers for policy enforcement
- Set up Kubernetes Horizontal Pod Autoscaler for dynamic resource scaling
- Implement Kubernetes cluster autoscaler for automatic node scaling
- Configure Kubernetes resource monitoring with Prometheus and Grafana
- Set up Kubernetes multi-tenancy with namespaces and RBAC
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Kubernetes Resource Quotas and Limit Ranges Installation Script
# Configures namespace-level resource management for Kubernetes clusters
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
NAMESPACE="${1:-production-app}"
SCRIPT_DIR="/tmp/k8s-resource-config"
# Usage message
usage() {
echo "Usage: $0 [namespace]"
echo " namespace: Kubernetes namespace to configure (default: production-app)"
exit 1
}
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
if [ $? -ne 0 ]; then
log_error "Script failed. Cleaning up..."
rm -rf "$SCRIPT_DIR"
if kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
kubectl delete namespace "$NAMESPACE" --ignore-not-found=true
log_info "Removed namespace: $NAMESPACE"
fi
fi
}
trap cleanup ERR
# Check if running as root or with sudo
check_privileges() {
if [ "$EUID" -ne 0 ] && ! sudo -n true 2>/dev/null; then
log_error "This script requires root privileges or sudo access"
exit 1
fi
}
# Auto-detect distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
log_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
log_info "Detected distribution: $ID ($VERSION_ID)"
}
# Check prerequisites
check_prerequisites() {
echo "[1/7] Checking prerequisites..."
# Check if kubectl is installed
if ! command -v kubectl >/dev/null 2>&1; then
log_error "kubectl is not installed or not in PATH"
log_info "Please install kubectl first: https://kubernetes.io/docs/tasks/tools/"
exit 1
fi
# Check if kubectl can connect to cluster
if ! kubectl cluster-info >/dev/null 2>&1; then
log_error "Cannot connect to Kubernetes cluster"
log_info "Please ensure your kubeconfig is properly configured"
exit 1
fi
log_success "Prerequisites check passed"
}
# Validate namespace name
validate_namespace() {
echo "[2/7] Validating namespace..."
if [[ ! "$NAMESPACE" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then
log_error "Invalid namespace name: $NAMESPACE"
log_info "Namespace must contain only lowercase letters, numbers, and hyphens"
exit 1
fi
if [ ${#NAMESPACE} -gt 63 ]; then
log_error "Namespace name too long (max 63 characters)"
exit 1
fi
log_success "Namespace name validated: $NAMESPACE"
}
# Create working directory and manifest files
create_manifests() {
echo "[3/7] Creating Kubernetes manifests..."
mkdir -p "$SCRIPT_DIR"
chmod 755 "$SCRIPT_DIR"
# Resource quota manifest
cat > "$SCRIPT_DIR/resource-quota.yaml" << EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: $NAMESPACE
spec:
hard:
# Compute resource limits
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
# Object count limits
pods: "10"
persistentvolumeclaims: "5"
services: "3"
secrets: "10"
configmaps: "10"
# Storage limits
requests.storage: "50Gi"
EOF
# Limit range manifest
cat > "$SCRIPT_DIR/limit-range.yaml" << EOF
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: $NAMESPACE
spec:
limits:
# Container-level limits
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
min:
cpu: "50m"
memory: "64Mi"
max:
cpu: "2"
memory: "4Gi"
# Pod-level limits
- type: Pod
min:
cpu: "100m"
memory: "128Mi"
max:
cpu: "4"
memory: "8Gi"
# Persistent volume limits
- type: PersistentVolumeClaim
min:
storage: "1Gi"
max:
storage: "20Gi"
EOF
# Test deployment manifest
cat > "$SCRIPT_DIR/test-deployment.yaml" << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-app
namespace: $NAMESPACE
spec:
replicas: 2
selector:
matchLabels:
app: test-app
template:
metadata:
labels:
app: test-app
spec:
containers:
- name: nginx
image: nginx:1.25
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
ports:
- containerPort: 80
EOF
chmod 644 "$SCRIPT_DIR"/*.yaml
log_success "Kubernetes manifests created"
}
# Create namespace
create_namespace() {
echo "[4/7] Creating namespace..."
if kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
log_warning "Namespace $NAMESPACE already exists"
else
kubectl create namespace "$NAMESPACE"
log_success "Namespace $NAMESPACE created"
fi
}
# Apply resource quota
apply_resource_quota() {
echo "[5/7] Applying resource quota..."
kubectl apply -f "$SCRIPT_DIR/resource-quota.yaml"
# Wait for quota to be created
sleep 2
log_success "Resource quota applied successfully"
}
# Apply limit range
apply_limit_range() {
echo "[6/7] Applying limit range..."
kubectl apply -f "$SCRIPT_DIR/limit-range.yaml"
# Wait for limit range to be created
sleep 2
log_success "Limit range applied successfully"
}
# Verify configuration
verify_configuration() {
echo "[7/7] Verifying configuration..."
# Check namespace exists
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
log_error "Namespace $NAMESPACE not found"
return 1
fi
# Check resource quota
if ! kubectl get quota production-quota -n "$NAMESPACE" >/dev/null 2>&1; then
log_error "Resource quota not found"
return 1
fi
# Check limit range
if ! kubectl get limitrange production-limits -n "$NAMESPACE" >/dev/null 2>&1; then
log_error "Limit range not found"
return 1
fi
# Display configuration status
log_info "Configuration Summary:"
echo "===================="
kubectl describe quota production-quota -n "$NAMESPACE" | grep -A 10 "Resource"
echo ""
kubectl describe limitrange production-limits -n "$NAMESPACE" | grep -A 15 "Type"
log_success "Resource quotas and limit ranges configured successfully!"
log_info "Namespace: $NAMESPACE"
log_info "Test deployment: kubectl apply -f $SCRIPT_DIR/test-deployment.yaml"
}
# Main function
main() {
log_info "Starting Kubernetes Resource Quotas and Limit Ranges configuration"
log_info "Target namespace: $NAMESPACE"
check_privileges
detect_distro
check_prerequisites
validate_namespace
create_manifests
create_namespace
apply_resource_quota
apply_limit_range
verify_configuration
# Cleanup temporary files
rm -rf "$SCRIPT_DIR"
log_success "Installation completed successfully!"
}
# Argument validation
if [ $# -gt 1 ]; then
usage
fi
# Run main function
main
Review the script before running. Execute with: bash install.sh