EKS CoreDNS Pods Pending or CrashLoopBackOff: "Too Many Pods", the Fargate Profile, the maxPods Math, the Corefile ready Plugin and the Add-on Update Conflicts, Fixed in Order
CoreDNS is the DNS server inside your EKS cluster: two pods in kube-system that every other pod asks when it looks up my-service.my-namespace.svc.cluster.local or api.stripe.com. When those two pods sit in Pending, nothing in the cluster can resolve a name, and when they go CrashLoopBackOff or stay unready, the symptom is the same. The causes are few and mechanical: there is no node for them to land on (a brand-new cluster, or a Fargate-only cluster where CoreDNS is still configured for EC2), the nodes are full (0/2 nodes are available: 2 Too many pods), the subnet is out of IP addresses, a custom Corefile is missing the ready plugin the readiness probe needs, or an add-on update collided with a PodDisruptionBudget or a hand-edited ConfigMap. Here is the number that surprises most people on the "Too many pods" branch: a t3.small node can run 11 pods, a t3.medium 17, counting the DaemonSets, because the limit is set by the instance type's network interfaces, not by CPU or memory. This page walks the causes in the order they appear, with the kubectl describe event for each, the Fargate profile command, the maxPods math and the prefix-delegation fix, the add-on version table, and the update commands that resolve the conflicts.
Ethan is on a contract where the client's new EKS cluster came up with every application pod in ContainerCreating and the two coredns pods in Pending for forty minutes. Jake, who has the phone repair shop and the habit of asking the plain question, is on speaker: "What is the cluster's DNS pod waiting for?" Ethan: "A computer to run on. The cluster has a control plane and a bill, and so far nothing else." The rest of this page is what he did next.
What CoreDNS is on EKS, and why it is the first thing to break
When you create an EKS cluster with at least one node, EKS deploys CoreDNS as a Deployment of two replicas in kube-system, labeled k8s-app=kube-dns for historical reasons, behind the kube-dns Service at the cluster's DNS IP. Every pod's /etc/resolv.conf points at that IP. Resolution of Services, of other pods, and of outside names (which CoreDNS forwards upstream to the VPC resolver) all go through those two pods. EKS manages CoreDNS as an add-on: a versioned component you create, update and configure through the EKS API, with a version pinned per Kubernetes version:
| Kubernetes version | Latest CoreDNS EKS add-on version |
|---|---|
| 1.37, 1.36, 1.35 | v1.14.7-eksbuild.10 |
| 1.34 | v1.13.2-eksbuild.50 |
| 1.33 | v1.12.4-eksbuild.57 |
| 1.32, 1.31 | v1.11.4-eksbuild.79 |
Two properties of that Deployment explain most Pending cases. It is configured to run on EC2 infrastructure by default, with tolerations for the control-plane taint and a topology spread across Availability Zones (whenUnsatisfiable: ScheduleAnyway, so the spread is a preference, not a block). And it is subject to the same scheduler as everything else: a node has to exist, have a free pod slot, a free IP address and the requested CPU and memory, or the pod waits. CoreDNS is just the first pod you notice, because without it the ones that did schedule cannot resolve anything.
Ethan to Jake: "DNS is not special; it is simply the first tenant. If the building has no floors, the first tenant waits in the lobby with everyone else." Jake: "So why does the cluster exist without floors?" Ethan: "Because the control plane is a separate thing you pay for, and the nodes are a second thing you create. Someone created the first and not the second."
Read the event: the one command that names the cause
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl describe pod -n kube-system -l k8s-app=kube-dns | sed -n '/^Events/,$p'
kubectl get nodes -o wide
kubectl get events -n kube-system --sort-by=.lastTimestamp | tail -20
The last Event line is the diagnosis. Match it here, then jump to the section:
| Event text | Meaning | Section |
|---|---|---|
FailedScheduling ... no nodes available to schedule pods | The cluster has zero Ready nodes, or only Fargate and no profile matches CoreDNS | No nodes, Fargate |
FailedScheduling ... fargate-scheduler Pod provisioning timed out (will retry) | A Fargate profile matched, but Fargate could not place the pod: subnets, pod execution role, or capacity | Fargate |
FailedScheduling ... 0/2 nodes are available: 2 Too many pods | Every node is at its maxPods | maxPods |
FailedScheduling ... Insufficient cpu or Insufficient memory | The nodes have slots but no resources left for CoreDNS's requests | Capacity |
FailedCreatePodSandBox ... failed to assign an IP address to container (pod stuck ContainerCreating) | The VPC CNI has no free IPs: the subnet is exhausted, or the node's ENIs are full | IPs |
Pod Running but 0/1 Ready; probe fails on /ready | Custom Corefile without the ready plugin, or CoreDNS cannot reach upstream | Ready and crashes |
CrashLoopBackOff with a Corefile parse error in the logs | A bad edit to the coredns ConfigMap | Ready and crashes |
| Add-on update fails; pods stay on the old version | PodDisruptionBudget conflict or a ConfigMap the add-on refuses to overwrite | Updates |
No nodes yet: the brand-new cluster case
A cluster created through the console, CloudFormation, Terraform or the CLI starts with a control plane and nothing else. CoreDNS is deployed immediately, so for the minutes (or days) between "cluster Active" and "node group Active" it sits Pending with no nodes available to schedule pods. kubectl get nodes returning nothing is the whole diagnosis. Create a managed node group, wait for its nodes to reach Ready, and CoreDNS schedules itself; there is nothing to restart.
If nodes exist but are NotReady, CoreDNS is a victim, not the cause: our NotReady guide walks the node-side checks (the node role, the VPC CNI, the subnet, the security groups, the cluster endpoint). One relevant detail from the cluster side: EKS needs at least six free IP addresses (sixteen recommended) in each subnet you gave the cluster, for its own network interfaces, and reports IP_NOT_AVAILABLE in the cluster's health issues when it cannot get them.
Fargate-only clusters: CoreDNS is still looking for EC2
This is the classic. You created a cluster with no node group because everything will run on Fargate, you created a Fargate profile for your application namespace, and CoreDNS stays Pending forever, because the CoreDNS Deployment targets EC2 by default and no Fargate profile selects kube-system. The fix is a profile that matches CoreDNS's label, then a restart so the pods are re-created under it:
- Create (or find) a Pod execution role, the IAM role Fargate uses to pull images and write logs. Its ARN must be
arn:aws:iam::ACCOUNT:role/NAMEwith no path. - Create a Fargate profile for CoreDNS, selecting
kube-systemwith the label, in private subnets:aws eks create-fargate-profile \ --fargate-profile-name coredns \ --cluster-name my-cluster \ --pod-execution-role-arn arn:aws:iam::111122223333:role/AmazonEKSFargatePodExecutionRole \ --selectors namespace=kube-system,labels={k8s-app=kube-dns} \ --subnets subnet-aaa subnet-bbb subnet-ccc - Restart the Deployment so the existing pods are replaced by ones the Fargate scheduler picks up:
kubectl rollout restart -n kube-system deployment coredns kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide # NODE column shows fargate-ip-... - Repeat the restart for any other Deployment that was created before its profile existed.
Clusters created with eksctl create cluster --fargate get this done for you. Older write-ups tell you to patch an eks.amazonaws.com/compute-type annotation out of the CoreDNS pod template; the profile-plus-restart procedure is the current one, and the annotation step is not part of it.
If the Event reads fargate-scheduler Pod provisioning timed out (will retry), a profile did match, and Fargate itself could not provision: the profile's subnets are public (Fargate pods need private subnets with a route out through a NAT gateway or endpoints), the pod execution role is missing its trust or permissions, or the subnet has no free addresses. Fix the profile's subnets, confirm the role, and the retry succeeds on its own.
"Too many pods": the maxPods math and why small nodes fill up fast
Every pod on an EC2 node gets its own VPC IP address, handed out by the VPC CNI from the node's elastic network interfaces. So the number of pods a node can hold is a network number, not a CPU number, and the default formula is:
maxPods = ENIs × (IPv4 addresses per ENI − 1) + 2
The −1 is the primary address of each interface, which belongs to the node; the +2 is for the VPC CNI and kube-proxy pods, which use the host network. Applied to common node sizes:
| Instance type | ENIs | IPv4 per ENI | maxPods (ENI formula) |
|---|---|---|---|
| t3.micro | 2 | 2 | 4 |
| t3.small, t4g.small | 3 | 4 | 11 |
| t3.medium, t4g.medium | 3 | 6 | 17 |
| t3.large, t4g.large | 3 | 12 | 35 |
| m5.large, m6i.large, m7i.large, m6g.large, m7g.large | 3 | 10 | 29 |
| t3.xlarge, m5.xlarge, m6i.xlarge, m7i.xlarge | 4 | 15 | 58 |
Now count what a node carries before your application: aws-node (the CNI), kube-proxy, CoreDNS when it lands there, plus whatever DaemonSets you added (a log shipper, a metrics agent, a security agent). On a t3.small, five system pods leave six slots; three such nodes give eighteen pods total, and a modest application with a few replicas each is out of room, which is when the scheduler prints 0/3 nodes are available: 3 Too many pods, and the next CoreDNS replica waits.
The precedence that decides the actual number on your node, highest first: a managed node group without a custom AMI injects a cap into the node's user data, 110 pods for instances under 30 vCPUs and 250 above, and that cap overrides anything else; a maxPods set directly in the kubelet configuration (custom AMI plus launch template); a maxPodsExpression in the nodeadm NodeConfig on self-managed nodes; and finally the ENI formula. The cap is a ceiling, so a t3.small in a managed node group still gets 11, because the ENI formula is lower.
Three ways out, cheapest first:
- Bigger instances or more of them. An m5.large holds 29; two of them hold more than five t3.smalls and run fewer DaemonSet copies.
- Prefix delegation. With VPC CNI 1.9.0 or later on Nitro instance types, the CNI assigns /28 prefixes to interfaces instead of single addresses (
ENABLE_PREFIX_DELEGATION=trueon theaws-nodeDaemonSet, withWARM_PREFIX_TARGET), which multiplies the addresses per interface; create new node groups rather than rolling the old ones, and expect maxPods to rise to the managed cap of 110. Once prefixes are on, the CNI cannot be downgraded below 1.9.0 without draining every node group. - A custom maxPods through a launch template with a custom AMI, passing
--use-max-pods falseand--kubelet-extra-args '--max-pods=N'to the bootstrap script; only meaningful together with prefix delegation, since the IPs still have to exist.
Ethan's client had three t3.small nodes, a log shipper and a security agent as DaemonSets, and an application with eleven replicas. Jake did the arithmetic out loud: "Three times eleven is thirty-three, minus five system pods each is eighteen, minus eleven app pods is seven." Ethan: "Minus the two DNS pods is five, which is why it worked yesterday and stopped today when they bumped replicas to sixteen." The fix was two m5.large nodes.
Out of IP addresses: ContainerCreating instead of Pending
A close cousin shows up as ContainerCreating rather than Pending, with FailedCreatePodSandBox events that end in failed to assign an IP address to container. The scheduler found a slot, but the CNI could not get a VPC address: the subnet is full, or the node's interfaces have no spare addresses and a new interface cannot be attached. aws ec2 describe-subnets --subnet-ids subnet-aaa --query 'Subnets[].AvailableIpAddressCount' shows the subnet side; the aws-node pod's logs on that node show the CNI side. The cures are the same as for maxPods (prefix delegation, which also eases subnet pressure, or larger subnets through custom networking for pods), plus the obvious one: a /24 subnet with three other services in it was not going to hold a cluster.
Running but not Ready, or CrashLoopBackOff: the Corefile
Once CoreDNS schedules, two things can keep it from serving. The first is the readiness probe: since add-on versions v1.9.3-eksbuild.3 and v1.10.1-eksbuild.6, the Deployment probes /ready, which is served by the ready plugin in the Corefile. If you replaced the Corefile with your own (to add a conditional forwarder for an on-premises domain, say) and left ready out, the pod runs and stays unready, and the Service sends it no traffic. Put the plugin back:
kubectl -n kube-system get configmap coredns -o yaml | grep -n "ready\|health\|forward"
# the default server block carries: health { lameduck 5s } and ready alongside kubernetes, forward, cache, loop, reload, loadbalance
The second is a Corefile that does not parse, which shows as CrashLoopBackOff with the parse error in kubectl logs -n kube-system -l k8s-app=kube-dns --previous. Fix the syntax and the pods restart themselves. Two facts about editing the Corefile on EKS: the add-on fully manages data.Corefile, so a direct edit is overwritten on the next add-on update, and the right place for custom forwarders is the add-on's configuration values rather than the ConfigMap; and from v1.11.1-eksbuild.4 the CoreDNS image is a minimal EKS Distro image with no shell, so kubectl exec into a CoreDNS pod fails by design, which is not a crash.
If the pods are Ready and lookups still fail from your pods, test from inside the cluster before blaming CoreDNS: kubectl run -it --rm dnstest --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default. A failure to resolve a Service name is CoreDNS; a failure on an outside name only is the upstream path (the VPC resolver, the node's security group, a NAT route for the node), and a failure from one node only is that node's CNI or network policy.
Add-on updates, PodDisruptionBudgets and conflicts
In order:
- Read the installed version with
describe-addon. - Save the current Deployment to a file, so your settings survive a bad update.
- Update to the version in the table for your Kubernetes version with
--resolve-conflicts PRESERVE. - Watch
kubectl rollout status -n kube-system deployment corednsand the pods' Ready column.
The commands:
aws eks describe-addon --cluster-name my-cluster --addon-name coredns --query addon.addonVersion --output text
kubectl get deployment coredns -n kube-system -o yaml > aws-k8s-coredns-old.yaml # keep a copy
aws eks update-addon --cluster-name my-cluster --addon-name coredns \
--addon-version v1.14.7-eksbuild.10 --resolve-conflicts PRESERVE
# optional settings, validated against the schema:
aws eks describe-addon-configuration --addon-name coredns --addon-version v1.14.7-eksbuild.10
aws eks update-addon --cluster-name my-cluster --addon-name coredns \
--addon-version v1.14.7-eksbuild.10 --resolve-conflicts PRESERVE \
--configuration-values '{"replicaCount":3}'
An error instead of a version from describe-addon means CoreDNS on this cluster is the self-managed type (installed with the cluster by older tooling), and you create the add-on first with aws eks create-addon --cluster-name my-cluster --addon-name coredns --addon-version ..., adding --resolve-conflicts OVERWRITE if your hand-made settings clash with the add-on's defaults.
The update failures that leave pods on the old version have two usual causes. A PodDisruptionBudget you created earlier conflicts with the one the add-on now ships (versions v1.9.3-eksbuild.6 and later, v1.10.1-eksbuild.3): either update with --resolve-conflicts OVERWRITE after backing up your custom settings, or kubectl delete poddisruptionbudget coredns -n kube-system and retry. The add-on's own budget defaults to maxUnavailable: 1 and can be tuned through the add-on configuration (one of maxUnavailable or minAvailable, not both; setting enabled: false leaves the object in place until you delete it). The other cause is a Deployment or ConfigMap you edited by hand that PRESERVE refuses to touch; move the change into configuration values, or accept OVERWRITE and reapply.
Two more version-specific changes that surprise people after an upgrade: the toleration moved from node-role.kubernetes.io/master to node-role.kubernetes.io/control-plane (both are set in current versions), and a default topologySpreadConstraints that spreads replicas across zones when nodes in several zones exist.
Two replicas is the default, not the right number
Two CoreDNS pods are deployed regardless of cluster size. On a busy cluster they become the bottleneck, with slow or refused lookups that look like application bugs. The EKS add-on (CoreDNS v1.9 and later) can autoscale the Deployment: it computes the larger of nodes ÷ 16 and CPU cores ÷ 256, clamps the result between minReplicas and maxReplicas, scales up immediately on demand measured over ten-minute peaks, and scales down by a third every three minutes. nodesPerReplica and cpuCoresPerReplica lower those divisors when you want more replicas per node; they need the minimum add-on version for your Kubernetes version. For a fixed number, replicaCount in the configuration values, as in the update command above.
Jake: "So the second node you added gave the DNS pod a floor, and now you want three DNS pods?" Ethan: "Two for a cluster of two nodes is fine. The client's other cluster has forty nodes and still two DNS pods, and that one gets the three-in-the-morning page."
Fixes by symptom, in order
| Symptom | Check | Fix |
|---|---|---|
| Pending, "no nodes available" | kubectl get nodes is empty | Create a node group, or a Fargate profile for kube-system with k8s-app=kube-dns and restart the Deployment |
| Pending on a Fargate cluster, "Pod provisioning timed out" | Profile subnets public? Pod execution role present? | Private subnets in the profile; fix the role; wait for the retry |
| Pending, "Too many pods" | kubectl describe node | grep -A3 'Allocated\|pods' | Larger instances or more nodes; prefix delegation with VPC CNI 1.9.0+; trim DaemonSets |
| Pending, "Insufficient cpu/memory" | Node allocatable vs requests | More capacity, or lower requests elsewhere |
| ContainerCreating, "failed to assign an IP address" | Subnet AvailableIpAddressCount; aws-node logs | Prefix delegation, larger or additional subnets, custom networking |
| Running, 0/1 Ready | Corefile has ready? | Restore the plugin; move customizations to add-on configuration values |
| CrashLoopBackOff | kubectl logs --previous shows a Corefile error | Fix the syntax; the pods restart on their own |
| Add-on update fails | Existing PodDisruptionBudget or hand-edited objects | --resolve-conflicts OVERWRITE after a backup, or delete the budget and retry |
| Lookups slow or refused on a big cluster | Two replicas for many nodes | Enable the add-on autoscaler, or set replicaCount |
kubectl exec into CoreDNS fails | Image v1.11.1-eksbuild.4 or later has no shell | Expected; debug from a busybox pod instead |
Frequently asked questions about EKS CoreDNS Pending and CrashLoopBackOff
Why are CoreDNS pods Pending on a new EKS cluster?
There is no node to run them on. A new cluster has a control plane only; CoreDNS schedules as soon as a node group reaches Ready. On a Fargate-only cluster, create a Fargate profile for kube-system with the label k8s-app=kube-dns and restart the Deployment.
How do I run CoreDNS on Fargate?
Create a Fargate profile that selects namespace kube-system with labels k8s-app=kube-dns, using a Pod execution role (ARN without a path) and private subnets, then run kubectl rollout restart -n kube-system deployment coredns. Clusters created with eksctl --fargate already have this.
What does "0/2 nodes are available: 2 Too many pods" mean?
Every node has reached its maxPods, which on EC2 comes from ENIs × (IPs per ENI − 1) + 2: 11 on t3.small, 17 on t3.medium, 29 on m5.large, 58 on m5.xlarge. Add nodes, use larger instances, or enable prefix delegation.
How many pods can an EKS node run?
By the ENI formula: t3.micro 4, t3.small 11, t3.medium 17, t3.large 35, m5.large 29, m5.xlarge 58. Managed node groups cap the value at 110 (under 30 vCPUs) or 250; with prefix delegation the ENI limit stops binding and the cap applies.
What is prefix delegation on EKS?
A VPC CNI mode (1.9.0 or later, Nitro instances) that assigns /28 IP prefixes to network interfaces instead of single addresses, multiplying the pods a node can hold. Set ENABLE_PREFIX_DELEGATION=true on the aws-node DaemonSet and create new node groups; the CNI cannot then be downgraded below 1.9.0 without draining all nodes.
Why is CoreDNS Running but not Ready?
The readiness probe hits /ready, which the ready plugin serves. A custom Corefile that leaves out ready fails the probe every time. Restore the plugin, and keep customizations in the add-on configuration values, because the add-on overwrites data.Corefile on update.
Why is CoreDNS in CrashLoopBackOff?
Most often a Corefile that does not parse after a manual edit; kubectl logs --previous shows the error. Fix the syntax and the pods recover. Check the Event block for scheduling or image-pull causes if the logs are empty.
How do I check the CoreDNS add-on version on EKS?
aws eks describe-addon --cluster-name CLUSTER --addon-name coredns --query addon.addonVersion --output text. An error instead of a version means the self-managed type is installed; create the EKS add-on with aws eks create-addon.
Which CoreDNS version does my Kubernetes version need?
v1.14.7-eksbuild.10 for Kubernetes 1.35 to 1.37, v1.13.2-eksbuild.50 for 1.34, v1.12.4-eksbuild.57 for 1.33, and v1.11.4-eksbuild.79 for 1.31 and 1.32.
Why does the CoreDNS add-on update fail?
A PodDisruptionBudget you created conflicts with the one newer versions ship, or hand-edited objects block --resolve-conflicts PRESERVE. Back up your settings, use --resolve-conflicts OVERWRITE, or delete the poddisruptionbudget coredns in kube-system and retry.
Can I edit the CoreDNS ConfigMap on EKS?
You can, but the add-on manages data.Corefile and overwrites it at the next update. Put forwarders and other changes in the add-on's configuration values instead.
How do I scale CoreDNS on EKS?
Use the add-on's autoscaler (CoreDNS v1.9 and later): replicas = max(nodes ÷ 16, cores ÷ 256) clamped between minReplicas and maxReplicas, tunable with nodesPerReplica and cpuCoresPerReplica. Or set a fixed replicaCount in the configuration values.
How do I test DNS inside an EKS cluster?
kubectl run -it --rm dnstest --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default. A Service name failing is CoreDNS; only outside names failing is the upstream path through the VPC resolver and the node's network.
Why can't I kubectl exec into a CoreDNS pod?
From add-on version v1.11.1-eksbuild.4 the image is a minimal EKS Distro image with no shell. That is by design; debug from a separate busybox pod.
How many free IPs does an EKS subnet need?
At least six, and sixteen recommended, in each subnet given to the cluster for its own interfaces; the cluster health issue IP_NOT_AVAILABLE appears otherwise. Pods need addresses on top of that, from the node subnets.
Does a managed node group change maxPods?
Yes: without a custom AMI it injects a cap of 110 pods for instances under 30 vCPUs and 250 above, overriding maxPodsExpression. To set your own value, use a custom AMI in the launch template with --use-max-pods false and --max-pods in the kubelet arguments.
For readers preparing for an AWS certification
We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.
Exams that test this topic: Solutions Architect – Associate (SAA-C03) • DevOps Engineer – Professional (DOP-C02) • Solutions Architect – Professional (SAP-C03 from November 17, 2026).
Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:
1. You want to run EKS pods without managing any nodes. What do you use?
AWS Fargate with a Fargate profile that selects the pods by namespace and labels.
2. Pods need AWS permissions without using the node's role. What do you use?
EKS Pod Identity or IAM roles for service accounts (IRSA).
3. Nodes must be added and removed automatically as pods come and go. What do you use?
Karpenter or the Kubernetes Cluster Autoscaler.
4. What limits how many pods fit on one node with the VPC CNI?
The number of network interfaces and IP addresses per interface for the instance type; prefix delegation raises it.
5. How do you keep CoreDNS, kube-proxy and the VPC CNI updated with little effort?
EKS managed add-ons, which update them for you and let you choose how to handle conflicts with your settings.
Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.
Ethan's forty-minute lobby ended with a managed node group of two m5.large nodes; CoreDNS scheduled within a minute of the first node going Ready, and the application pods followed. The second surprise came a week later on the same cluster, when the client doubled the replicas and the scheduler wrote "Too many pods," which is how the maxPods table on this page came to exist. Jake, on speaker again: "So the fix for the DNS was not about DNS at all." Ethan: "It rarely is. It is the first pod to tell you the floor is missing."
📌 If you keep one line from this page
CoreDNS Pending is a scheduling problem: no nodes (or no Fargate profile for kube-system), no pod slots (maxPods = ENIs × (IPs − 1) + 2), or no IPs; CoreDNS not Ready is a Corefile without the ready plugin; add-on update failures are a PodDisruptionBudget or a hand edit.
t3.small 11 pods · t3.medium 17 · m5.large 29 · managed node group cap 110/250 · prefix delegation with VPC CNI 1.9.0+ · describe-addon, update-addon --resolve-conflicts.
Revision note. Written October 16, 2026, from the EKS CoreDNS, Fargate, instance-type and VPC CNI pages and the EC2 network-interface table as they stood that week, with the scheduler events as kubectl prints them. "Look before you leap," says the proverb; here the looking is one kubectl describe before you touch anything.