Kubernetes Max Pods per Node Calculator
Work out the real pod ceiling for a node. On EKS with the VPC CNI it is ENI arithmetic rather than the kubelet's 110, and this also checks whether the subnet holds the addresses your nodes will ask for, because running out shows up as pods stuck in ContainerCreating. It is arithmetic on the numbers you type, not a measurement of a cluster.
max-pods.txt
updates as you type Common mistakes
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
Using the 110 default as the real limit
On EKS the ENI-based limit is usually far lower, and on other platforms the CNI decides. 110 is a kubelet default, not a capability.
Instead:Compute from the instance type and CNI, which is what this page does.
Forgetting the daemonsets
Every node runs the CNI, kube-proxy, logging and monitoring pods, and they come off the same budget.
Instead:Subtract them before dividing by pods per workload.
Sizing subnets after choosing the node type
On a CNI that gives every pod a VPC address, the subnet is the real ceiling and it cannot be resized.
Instead:Plan the subnet and the pod density together.
Why a node runs fewer pods than --max-pods says
The kubelet flag is a cap, not a promise. Where the network plugin gives every pod a real address out of a real subnet, the number of addresses the node can hold is a second cap, and on EKS without prefix delegation it is usually the lower of the two. The scheduler knows nothing about either subnet or addresses, so exhausting them does not look like a capacity problem: the pod is placed, accepted, and then sits in ContainerCreating.
Two ceilings, and the lower one is the answer
Every pod takes a slot in --max-pods and, on a plugin that hands out subnet addresses, an address as well. So the pods per node figure is the lower of the two, always. The kubelet default of 110 is a hard cap independent of how much CPU and memory the node has, which is why a very large node can be idle and full at once. On EKS the two numbers are also coupled in the other direction: the AMI bootstrap script reserves 255MiB plus 11MiB per pod slot, so a higher flag costs memory whether the slots are used or not.
address ceiling 29 pods
kubelet --max-pods 110 pods
---------
pods per node 29 pods bound by the
address ceiling
Raising the flag above 29 changes nothing except
the memory reservation. Lowering it below 29 is
the only direction that has any effect. The VPC CNI formula, and where the minus one and plus two come from
With the AWS VPC CNI, pod addresses come from the node's elastic network interfaces, so the ceiling is (ENIs x (IPv4 addresses per ENI - 1)) + 2. One address on each ENI is that interface's own primary and can never go to a pod, which is the subtraction. The two added back are aws-node and kube-proxy, which run on the host network and use the node's own address rather than one of their own. Both limits are per instance type, and neither is on the instance type page next to the vCPU count.
max pods = (ENIs x (IPv4 per ENI - 1)) + 2
t3.medium 3 ENIs, 6 each (3 x 5) + 2 = 17
m5.large 3 ENIs, 10 each (3 x 9) + 2 = 29
c5.large 3 ENIs, 10 each (3 x 9) + 2 = 29
m5.xlarge 4 ENIs, 15 each (4 x 14) + 2 = 58
m5.2xlarge 4 ENIs, 15 each (4 x 14) + 2 = 58
A t3.medium tops out at a sixth of the kubelet
default, and the two host-network pods are
already counted in the 17. Five instance types is not the list
The five in the form are the ones people ask about. AWS publishes the ENI and address limits for every instance type, in the EC2 documentation and in the eni-max-pods.txt file shipped with the EKS AMI, and the list changes as instance families are added. That is the reason this page does not carry a copy of it: a table baked into a tool page goes stale silently, and a stale row here would be a confidently wrong number. Take the two figures from the file AWS publishes and use the custom option.
# on any EKS node
cat /etc/eks/eni-max-pods.txt
# what the node ended up with, which is the
# only authority on that node
kubectl get node ip-10-0-1-23 \
-o jsonpath='{.status.allocatable.pods}' Prefix delegation changes the arithmetic rather than tuning it
With ENABLE_PREFIX_DELEGATION=true, each usable slot on an ENI holds a /28 prefix of 16 addresses instead of a single address, so the ceiling multiplies by roughly 16 and stops being the binding constraint. It needs nitro instances and VPC CNI 1.9 or later. The cost is fragmentation: the subnet must have contiguous /28 blocks free, and a subnet that has been in use for a year can have thousands of free addresses with no contiguous /28 among them, at which point allocation fails while the subnet looks half empty. AWS's guidance is to cap --max-pods at 110 for nodes with 30 or fewer vCPUs and 250 above that, so the flag becomes a number you set deliberately.
m5.large, prefix delegation off
(3 x 9) + 2 = 29 pods
m5.large, prefix delegation on
(3 x (9 x 16)) + 2 = 434 pods
so the kubelet flag binds instead, and on EKS
the flag is charged in memory:
--max-pods=110 -> 1465Mi reserved
--max-pods=250 -> 3005Mi reserved It surfaces as ContainerCreating, not as a scheduling failure
This is what makes address exhaustion expensive. The scheduler has no idea how many addresses are left, so it places the pod on the node, the kubelet accepts it, and the failure happens afterwards while the sandbox is being set up. The pod sits in ContainerCreating with an event reading that it failed to assign an IP address to the container. Nothing in that message says subnet, ENI or prefix, and nobody reading it thinks about subnet sizing, which is why this costs an afternoon rather than a minute.
kubectl get pod
NAME READY STATUS RESTARTS
api-7d9f 0/1 ContainerCreating 0
kubectl describe pod api-7d9f
failed to assign an IP address to container
# Not Pending. Not FailedScheduling. Not
# Insufficient cpu. The scheduler was happy. GKE, AKS with Azure CNI, and AKS with kubenet all count differently
GKE gives each node a pod CIDR of its own out of the cluster pod range and sizes it at roughly twice the maximum pods per node, so the addresses in the block are not all schedulable and a smaller block caps the node below the flag. AKS with Azure CNI in node subnet mode takes pod addresses from the same subnet as the node and reserves them when the node joins rather than when a pod starts, so the subnet has to be sized for a full node from day one. Azure CNI overlay behaves differently again, which is why the mode matters before any sizing. kubenet takes pod addresses from a separate CIDR, so the subnet holds one address per node, and the limit that bites instead is the route table.
GKE per-node /24 -> 256 addresses, halved to
128, so the default 110 fits
per-node /25 -> 128, halved to 64, and
64 is now the ceiling whatever the
flag says. Fixed for the node pool.
AKS, Azure CNI node subnet
nodes x (max pods + 1), pre-assigned
at node join. max pods is fixed when
the pool is created: getting it wrong
means a new pool and a migration.
AKS, kubenet
1 subnet address per node, and one
user-defined route per node against a
400 route table, so 400 nodes. Sizing the subnet is the other half of the question
AWS and Azure both reserve five addresses in every subnet, and neither lets you resize one after creation. So the sum that matters is nodes multiplied by the addresses each node takes, against what the subnet holds. On EKS the steady-state figure is the pods plus the node's own address, and the worst case is every ENI attached with its full set of addresses, which is what the subnet has to survive. When the sum does not fit, the fix is another subnet, and on EKS that means the CNI custom networking configuration rather than simply adding one.
a /24 holds 2^8 - 5 = 251 usable
20 nodes x (29 pods + 1) = 600
---
short by 349
worst case, m5.large with prefix delegation:
in use 111 addresses
every prefix 433 addresses
^ what the subnet must hold