Warrior, now we enter the Scaling & Reliability phase.
So far, Kubernetes knows:
Run 3 PodsBut Kubernetes also needs to know:
How much CPU and memory does each Pod need?
Without resource requests and limits, one badly behaved application could consume a huge amount of a node's resources and affect other workloads.
The two most important concepts are:
Requests → What the Pod needs
Limits → Maximum resource usage allowedImagine one Kubernetes node has:
CPU = 4 cores
Memory = 8 GBAnd we have:
Application A
Application B
Application CSuppose Application A suddenly consumes:
CPU = 3.8 cores
Memory = 7 GBThen:
Application B
Application C
↓
Could be starved of resourcesKubernetes needs a way to manage this.
That's where:
requests
limitscome in.
Kubernetes primarily manages:
CPU
MemoryFor containers.
Example:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Let's decode this.
CPU can be specified using:
1
500m
250m
100mm means:
millicpu
So:
1000m = 1 CPUTherefore:
500m = 0.5 CPU
250m = 0.25 CPU
100m = 0.1 CPUThink:
1 CPU
│
├──────────── 1000m
│
├────── 500m
│
└── 250mMemory can use units such as:
Mi
Gi
M
GFor Kubernetes resource quantities, you'll commonly encounter:
128Mi
256Mi
512Mi
1Gi
2GiFor example:
256Mi ≈ 256 MiB
1Gi ≈ 1 GiBFor practical Kubernetes work, remember:
Mi → mebibytes
Gi → gibibytesA request tells Kubernetes:
"I need at least this much resource for scheduling purposes."
Example:
resources:
requests:
cpu: "250m"
memory: "256Mi"This means Kubernetes considers:
CPU requirement = 250m
Memory requirement = 256Miwhen deciding where to place the Pod.
Suppose:
Node A
CPU available for scheduling = 500mYour Pod requests:
CPU = 250mIt can potentially fit.
But if:
Node B
CPU available = 100mthen the Pod cannot fit based on its CPU request.
Conceptually:
Pod
Request:
250m CPU
│
▼
Scheduler
│
├── Node A → 500m available ✅
│
└── Node B → 100m available ❌This is one of the most important purposes of requests.
A limit defines the maximum resource usage allowed for a container.
Example:
resources:
limits:
cpu: "500m"
memory: "512Mi"Meaning:
CPU
↓
Can use up to approximately 0.5 CPU
Memory
↓
Can use up to 512MiMemorize this:
Request
↓
Used mainly for scheduling
Limit
↓
Maximum allowed usageFor example:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Conceptually:
CPU:
0 ─────── 250m ───────────── 500m
↑ ↑
Request LimitImagine a Node.js API.
Normal usage:
CPU = 150m
Memory = 200MiDuring a traffic spike:
CPU = 400m
Memory = 400MiWe could configure:
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"So Kubernetes knows:
Normal reservation:
200m CPU
256Mi Memory
Maximum:
500m CPU
512Mi MemorySuppose we have:
Node
CPU = 4
Memory = 8GiWe already run:
Pod A
requests:
cpu: 1
memory: 2Gi
Pod B
requests:
cpu: 1
memory: 2GiThen scheduler sees approximately:
Requested CPU = 2
Requested memory = 4GiRemaining schedulable capacity is approximately:
CPU = 2
Memory = 4GiA new Pod requesting:
CPU = 3cannot fit on that node.
Even if actual CPU usage happens to be low at that moment.
This is important:
Scheduling is based primarily on resource requests, not current instantaneous usage.
Suppose your application has a memory leak.
Without an appropriate memory limit:
Node
│
├── API
│
├── Redis
│
├── Frontend
│
└── Bad application
│
├── 1GB
├── 2GB
├── 4GB
└── 8GB...It could consume too much node memory.
With a limit:
memory limit = 512Mithe container can't simply keep consuming memory indefinitely.
This is extremely important.
Suppose:
limits:
memory: "512Mi"Application tries to consume:
700MiThe container may be terminated due to an out-of-memory condition.
You may see:
OOMKilledCheck:
kubectl describe pod <pod-name>You may find:
Reason: OOMKilledThen:
kubectl get pod <pod-name>could show restart behavior.
CPU and memory behave differently.
CPU is generally compressible.
If the container tries to exceed its CPU limit, it can be throttled.
Conceptually:
CPU limit = 500m
Application wants:
800m
Kubernetes:
"You're limited to approximately 500m."The application may become slower.
Memory is not compressible in the same way.
If a container exceeds its memory limit:
Memory limit = 512Mi
Application wants:
700Mithe container can be terminated with:
OOMKilledSo:
CPU → throttling
Memory → potential OOM killis a useful interview-level mental model.
A realistic Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 3
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: my-node-api:1.0
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Now each Pod requests:
250m CPU
256Mi memoryand can use up to:
500m CPU
512Mi memorysubject to Kubernetes/runtime behavior and node capacity.
Request = 250m
Limit = 500m
Actual CPU = 150mEverything is fine.
Request = 250m
Limit = 500m
Actual CPU = 450mStill within the limit.
Request = 250m
Limit = 500m
Application wants >500mCPU can be throttled.
You can do:
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"This has consequences for the Pod's QoS classification, which we'll discuss shortly.
You might write:
containers:
- name: app
image: my-appwith no:
resources:Then your workload may have no explicit resource requests/limits.
This can create problems:
❌ Scheduling becomes less predictable
❌ One workload may consume excessive resources
❌ HPA behavior can be affected
❌ Cluster capacity planning becomes harder
❌ Troubleshooting becomes harderIn production, explicitly defining resource requirements is generally a good practice.
Kubernetes assigns Pods a Quality of Service class.
There are three:
Guaranteed
Burstable
BestEffortThese affect how Kubernetes treats Pods under resource pressure.
If containers have:
No CPU requests
No CPU limits
No memory requests
No memory limitsthe Pod can be classified as:
BestEffortExample:
containers:
- name: app
image: nginxNo resources specified.
A Pod is generally Burstable when it has some resource requests/limits but doesn't meet the conditions for Guaranteed QoS.
Example:
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"Request and limit are different.
This is common for many applications.
A Pod is generally Guaranteed when every container has CPU and memory requests and limits specified, and for each resource:
request = limitExample:
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"for each container in the Pod.
QoS
│
┌──────────┼──────────┐
│ │ │
Guaranteed Burstable BestEffort
│ │ │
request= Some Nothing
limit resources specifiedUnder node resource pressure, QoS affects eviction behavior, but don't think of QoS as a simple "priority" system. Actual eviction decisions also consider resource pressure and Pod-level signals such as priority.
We briefly saw this in Chapter 8.
Now it makes more sense.
Suppose namespace:
developmentshould have a maximum:
CPU requests = 4
Memory requests = 8Gi
Pods = 20You can use:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: development
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
pods: "20"Now Kubernetes can enforce namespace-level resource consumption limits.
Don't confuse them.
Pod/container levelExample:
Container max memory = 512MiNamespace levelExample:
Namespace total memory requests <= 8GiThink:
Container
↓
resources.requests / limits
Namespace
↓
ResourceQuotaA LimitRange can define defaults and constraints for resources inside a namespace.
Example:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: development
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"Now if developers forget to specify resources, Kubernetes can apply configured defaults.
This is useful for enforcing consistency.
A mature namespace can use both:
Namespace
│
┌───────────┴───────────┐
│ │
ResourceQuota LimitRange
│ │
Total namespace Per-container
capacity defaults/limitsExample:
Namespace
│
├── Maximum 20 Pods
├── Maximum 8Gi memory requests
│
└── Each container:
default request = 128Mi
default limit = 512MiThis is worth understanding deeply.
Suppose:
Node A
CPU capacity = 4Existing Pods request:
Pod 1 → 1 CPU
Pod 2 → 1 CPU
Pod 3 → 500mTotal:
2.5 CPUNew Pod requests:
1.5 CPUThen:
2.5 + 1.5 = 4 CPUIt can potentially fit.
But if the new Pod requests:
2 CPUthen:
2.5 + 2 = 4.5 CPUIt doesn't fit on that node based on CPU requests.
The scheduler may try another node.
Pending Due to Insufficient Resources#Imagine you deploy:
resources:
requests:
cpu: "8"
memory: "32Gi"but your cluster has no node capable of satisfying that request.
The Pod can remain:
PendingCheck:
kubectl get podsThen:
kubectl describe pod <pod-name>You might see an event such as:
Insufficient cpuor:
Insufficient memoryThis is a very common Kubernetes troubleshooting scenario.
Suppose:
requests:
cpu: "500m"This does not mean your application continuously consumes exactly 500m CPU.
It means:
Kubernetes uses that value as the requested resource amount for scheduling and capacity management.
Actual usage could be:
100m
200m
450metc.
This becomes especially important in the next chapter.
Suppose HPA is configured based on CPU utilization.
It may calculate utilization relative to the container's CPU request.
For example:
CPU request = 250m
Actual usage = 200mUtilization:
200 / 250 = 80%So a good CPU request matters not only for scheduling but also for autoscaling behavior.
We'll cover HPA in Chapter 15.
Suppose:
Node:
4 CPU
8Gi memoryYou might think:
Pod requests:
4 CPU
8Gi memorymeans the Pod can definitely consume everything.
Not exactly.
Requests are used for scheduling; limits determine maximum usage at the container level.
Also, Kubernetes nodes need resources for system components and other overhead.
So actual allocatable capacity is not simply identical to the raw hardware capacity.
Suppose your Node.js API normally needs:
100–200m CPUYou might start with:
requests:
cpu: "200m"and allow bursts:
limits:
cpu: "1"So:
Normal reservation:
200m
Burst:
up to 1 CPUBut don't blindly copy these values into production.
You should measure actual workload behavior.
Node.js applications can consume substantial memory depending on:
Traffic
Heap size
Caching
Large JSON processing
File processing
LLM workloadsFor example:
requests:
memory: "512Mi"
limits:
memory: "1Gi"If your application genuinely needs more than 1Gi under normal workload, setting the limit too low can cause:
OOMKilledTherefore:
Resource limits should be based on observed application behavior, not arbitrary numbers.
This is especially relevant for AI workloads.
Suppose you eventually run a model-serving container:
LLM serverand it needs:
CPU
RAM
GPUCPU/memory resource configuration alone isn't enough for GPUs.
Kubernetes supports GPU resources through device plugins and vendor integrations, with resources such as:
nvidia.com/gpuFor example, conceptually:
resources:
limits:
nvidia.com/gpu: 1GPU scheduling is a more advanced topic, but remember:
Kubernetes can schedule specialized resources in addition to CPU and memory.
kubectl describe pod <pod>kubectl describe node <node>If Metrics Server is installed:
kubectl top podsand:
kubectl top nodesExample:
NAME CPU(cores) MEMORY(bytes)
node-api 180m 220MiThis shows current usage, not requests/limits.
kubectl top vs Requests#Don't confuse:
kubectl top podswith:
resources.requestskubectl top:
Actual observed usageRequests:
Declared scheduling requirementLimits:
Declared maximumSo:
Usage ≠ Request ≠ LimitThis is an excellent interview concept.
Here's a reasonable example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 3
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: my-node-api:1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"With three replicas, the scheduler considers approximately:
CPU requests:
3 × 250m = 750m
Memory requests:
3 × 256Mi = 768MiThat's the requested capacity for the three containers, not necessarily their actual usage.
limits:
memory: "128Mi"for an application that normally requires 300Mi.
Result:
OOMKilledHuge requests:
requests:
cpu: "8"for a small application.
Result:
Pod may remain Pendingbecause Kubernetes cannot find a node with enough allocatable CPU.
No requests/limits for production workloads.
This makes capacity planning and scheduling less predictable.
Copying resource values from another application.
Your:
Node.js APIdoesn't necessarily have the same requirements as:
PostgreSQLor:
Next.jsMeasure your workload.
The amount of CPU or memory a container requests for scheduling and resource planning.
The maximum CPU or memory usage configured for a container.
Generally, CPU usage can be throttled.
The container can be terminated with an out-of-memory condition, commonly observed as OOMKilled.
Primarily requests when deciding whether a Pod fits on a node.
A Pod with no CPU/memory requests or limits.
A Pod that has some resource requests/limits but doesn't meet the Guaranteed QoS criteria.
Generally, every container has CPU and memory requests and limits, with requests equal to limits for those resources.
A namespace-level mechanism for limiting aggregate resource consumption.
A namespace-level mechanism for defining default and/or minimum/maximum resource settings for containers or Pods.
The four most important things:
REQUEST
↓
Used for scheduling
LIMIT
↓
Maximum configured usage
CPU
↓
Can be throttled
MEMORY
↓
Can result in OOMKilledRemember:
Actual Usage
≠
Request
≠
LimitAnd:
Pod
│
├── requests → Scheduler
│
└── limits → Runtime enforcementYou now have:
INTERNET
│
▼
Ingress
│
▼
Service
│
┌──────┼──────┐
▼ ▼ ▼
Pod Pod Pod
│
┌──────┴──────┐
│ │
ConfigMap Secret
│ │
└──────┬──────┘
│
Container
│
┌─────┴─────┐
│ │
CPU Memory
Request Request
Limit LimitAnd if the application needs persistent data:
Container
│
▼
Volume
│
▼
PVC
│
▼
PV
│
▼
Persistent StorageThis chapter is extremely important for zero-downtime production systems.
You'll learn:
Liveness Probe
Readiness Probe
Startup Probe
HTTP Probe
TCP Probe
Command Probe
initialDelaySeconds
periodSeconds
timeoutSeconds
failureThresholdAnd we'll answer the critical question:
How does Kubernetes know whether your Node.js application is alive, ready to receive traffic, or still starting?
Mark it as complete to track your mastery.