A cluster with no worker capacity can schedule nothing. EKS gives you two ways to provide that capacity, and both are created through the EKS SDK: managed node groups (EC2 instances EKS runs for you) and Fargate profiles (serverless capacity billed per pod).
This page shows how to create and operate each, and when to reach for which. Which pods actually land on the capacity is still the Kubernetes scheduler's call - the SDK only provisions the capacity.
A managed node group is a set of EC2 instances that EKS provisions, bootstraps, joins to the cluster, and later upgrades on your behalf. CreateNodegroup needs the node IAM role, subnets, and a scalingConfig.
The labels become Kubernetes node labels, so workloads can target this group with a node selector. amiType picks the managed AMI family (Amazon Linux 2023, Bottlerocket, or a GPU variant), and EKS keeps that AMI patched when you trigger an upgrade.
Capacity needs change. UpdateNodegroupConfig resizes a group without recreating it, and UpdateNodegroupVersion rolls the AMI to a newer Kubernetes or AMI release, replacing instances gradually.
# --- Python (boto3) ---import boto3eks = boto3.client("eks", region_name="us-east-1")# Resize the group in place.eks.update_nodegroup_config( clusterName="platform", nodegroupName="general", scalingConfig={"minSize": 3, "maxSize": 10, "desiredSize": 4},)# Roll the AMI/K8s version with a gradual, health-aware replacement.eks.update_nodegroup_version( clusterName="platform", nodegroupName="general", version="1.30",)
// --- TypeScript (AWS SDK v3) ---import { EKSClient, UpdateNodegroupConfigCommand, UpdateNodegroupVersionCommand,} from "@aws-sdk/client-eks";const eks = new EKSClient({ region: "us-east-1" });// Resize the group in place.await eks.send(new UpdateNodegroupConfigCommand({ clusterName: "platform", nodegroupName: "general", scalingConfig: { minSize: 3, maxSize: 10, desiredSize: 4 },}));// Roll the AMI/K8s version with a gradual, health-aware replacement.await eks.send(new UpdateNodegroupVersionCommand({ clusterName: "platform", nodegroupName: "general", version: "1.30",}));
An update returns an update id and runs asynchronously. UpdateNodegroupVersion cordons and drains old nodes and brings up replacements while respecting a maximum-unavailable setting, so a version bump does not take the whole group down at once. desiredSize is a starting point - if you run the Kubernetes Cluster Autoscaler or Karpenter, that controller adjusts the desired count within the min/max you set here.
A Fargate profile is the serverless alternative: instead of you running EC2 nodes, AWS runs each matching pod on right-sized, isolated capacity and bills per pod. You do not manage or scale nodes at all. The trade-off is per-pod pricing and some feature limits (no DaemonSets, no privileged pods, no host networking).
A profile is a selector: it says "pods in these namespaces, optionally with these labels, run on Fargate." CreateFargateProfile needs a pod execution role and private subnets.
A pod matches the profile if its namespace matches and, when labels are given, all of those labels match. A pod in web with compute: fargate runs on Fargate; a pod in batch runs on Fargate regardless of labels; anything not matching any profile falls to your node groups. The pod execution role is what the Fargate infrastructure uses to pull images and write logs, and it must trust eks-fargate-pods.amazonaws.com.
Node groups win when you need DaemonSets (log/metric agents, the CNI), GPU or specialized instances, control over bin-packing, or the lowest cost at steady high utilization. Fargate wins for spiky or bursty workloads, strong per-pod isolation, and eliminating node operations entirely. Many clusters run both: node groups for the system add-ons and steady tier, Fargate profiles for isolated or bursty namespaces.
Fargate needs private subnets.CreateFargateProfile rejects public subnets. Provide subnets with a route to a NAT gateway, not an internet gateway.
DaemonSets do not run on Fargate. Each Fargate pod is its own micro-VM, so per-node DaemonSets (including some logging agents) have no node to attach to. Use sidecars or node groups for those.
Wrong role on the wrong resource. A node group takes a node role (trusts ec2.amazonaws.com); a Fargate profile takes a pod execution role (trusts eks-fargate-pods.amazonaws.com). Swapping them fails.
desiredSize fights the autoscaler. If a controller manages the count, setting desiredSize by hand can cause a tug-of-war. Let the autoscaler own it within min/max.
Deleting a node group is not instant. It cordons, drains, and terminates instances; wait on nodegroup_deleted before deleting the cluster.
Selectors are matched most-specific-first across profiles. A pod matching multiple profiles uses the first match; keep selectors non-overlapping to stay predictable.
Self-managed node groups (plain EC2 Auto Scaling groups joined to the cluster) when you need bootstrap customizations beyond what managed node groups expose.
Karpenter for just-in-time node provisioning that picks instance types per pending pod, often replacing static node groups for the scalable tier.
Cluster Autoscaler to scale managed node groups reactively within their min/max instead of setting desiredSize by hand.
IaC (eksctl, CDK, Terraform) to declare node groups and Fargate profiles as versioned infrastructure, with the SDK for runtime resizing.
Reach for node groups when you want control and steady-state cost efficiency, Fargate when you want to stop managing nodes, and a controller like Karpenter or Cluster Autoscaler when capacity should track demand automatically.
A set of EC2 worker instances that EKS provisions, bootstraps, joins to the cluster, and upgrades for you. You create it with CreateNodegroup and resize it with UpdateNodegroupConfig.
How do I resize a node group?
Call UpdateNodegroupConfig with a new scalingConfig (minSize, maxSize, desiredSize). It changes the group in place without recreating it.
How do I upgrade the AMI or Kubernetes version of a node group?
Use UpdateNodegroupVersion. It cordons and drains old nodes and brings up replacements gradually, honoring a maximum-unavailable setting.
What is a Fargate profile?
A selector that routes matching pods (by namespace and optional labels) to serverless Fargate capacity billed per pod, so you run no EC2 nodes for those pods. Create it with CreateFargateProfile.
Why does my Fargate profile creation fail on subnets?
Fargate profiles require private subnets. A subnet with a route to an internet gateway (a public subnet) is rejected; use subnets routed through a NAT gateway.
What role does a Fargate profile need?
A pod execution role that trusts eks-fargate-pods.amazonaws.com and can pull images and write logs. It is different from the EC2 node role a node group uses.
Can I run DaemonSets on Fargate?
No. Each Fargate pod runs in its own isolated micro-VM with no shared node, so per-node DaemonSets do not apply. Use sidecars, or run those workloads on node groups.
What happens to a pod that matches no Fargate profile?
It is scheduled onto your managed or self-managed node groups like a normal pod. Fargate only claims pods that match a profile's selectors.
Should I set desiredSize if I run an autoscaler?
Let the autoscaler own the desired count within the min/max you set. Manually setting desiredSize can conflict with the controller and cause thrashing.
Can one cluster use both node groups and Fargate?
Yes, and many do. A common split is node groups for system add-ons and steady workloads, plus Fargate profiles for bursty or strongly isolated namespaces.