EKS Fargate Deep Dive: When to Use It and When to Stay on Managed Nodes

Bits Lovers
Written by Bits Lovers on
EKS Fargate Deep Dive: When to Use It and When to Stay on Managed Nodes

A batch job that runs for eight minutes, three times a day. A CI pipeline that spins up test pods on every commit. An API that handles zero traffic on weekends. These are the workloads where Fargate’s model makes financial sense. For a persistent service that runs 24/7 at steady load, managed EC2 nodes will almost always be cheaper. Understanding the split is the whole point — Fargate isn’t a replacement for managed nodes, it’s a complement to them.

EKS Fargate runs pods on AWS-managed infrastructure. No EC2 instances to provision, no AMIs to keep updated, no kubelet to configure. You define a Fargate profile, and pods matching that profile’s namespace and label selectors get scheduled automatically onto Fargate compute. AWS handles the underlying host, the OS patching, and the kubelet running on it.

Fargate Profiles: How Pod Scheduling Works

A Fargate profile tells EKS which pods should run on Fargate. Each profile specifies:

  • One or more namespaces the profile applies to
  • Optional label selectors to filter pods within those namespaces
  • A pod execution role — the IAM role Fargate uses to pull images and write logs
  • One or more subnets where Fargate pods get scheduled
# Create a Fargate profile with eksctl
eksctl create fargateprofile \
  --cluster my-cluster \
  --name batch-jobs \
  --namespace batch \
  --labels workload=batch

Or declaratively:

# cluster.yaml
fargateProfiles:
  - name: batch-jobs
    selectors:
      - namespace: batch
        labels:
          workload: batch
  - name: system-pods
    selectors:
      - namespace: kube-system
      - namespace: aws-observability

Pods that match a profile get scheduled onto Fargate. Pods that don’t match any profile continue to be scheduled onto EC2 node groups. The two coexist in the same cluster — you can run stateful, GPU, or DaemonSet-dependent workloads on managed nodes while routing batch jobs and burstable services to Fargate.

One important scheduling detail: pods must fit within a single Fargate host. There’s no pod-to-pod scheduling the way EC2 nodes bin-pack multiple pods. Each Fargate pod runs in isolation on its own dedicated micro-VM. That’s a security property (no noisy neighbor at the kernel level) but also a capacity planning constraint — your resource requests need to reflect actual usage since you’re paying for whatever the host allocates to match your pod spec.

Pod Sizing and Resource Allocation

Fargate bills per vCPU and GB of memory, rounded up to the nearest valid combination. The minimum is 0.25 vCPU with 0.5 GB, and the maximum is 16 vCPU with 120 GB. Between those bounds, Fargate allocates to the nearest supported combination — it rounds up your requests to the next valid size.

Valid vCPU/memory pairings (selected examples):

vCPU Memory Range
0.25 0.5 GB – 2 GB
0.5 1 GB – 4 GB
1 2 GB – 8 GB
2 4 GB – 16 GB
4 8 GB – 30 GB
8 16 GB – 60 GB
16 32 GB – 120 GB

Always set explicit resource requests. Without them, Fargate allocates the minimum (0.25 vCPU, 0.5 GB) which is almost never right, and your pod gets OOMKilled or runs far slower than expected. Set requests that reflect what the application actually needs:

spec:
  containers:
    - name: worker
      image: my-batch-worker:latest
      resources:
        requests:
          cpu: "1"
          memory: "2Gi"
        limits:
          cpu: "1"
          memory: "2Gi"

Setting requests equal to limits on Fargate avoids the rounding ambiguity — Fargate allocates exactly to the request level, and the limit prevents the container from claiming more than the host provides.

Pricing: Where Fargate Wins and Loses

Fargate charges per vCPU-second and per GB-second. At us-east-1 rates:

  • vCPU: $0.04048 per vCPU-hour
  • Memory: $0.004445 per GB-hour

A pod with 1 vCPU and 2 GB memory costs roughly $0.049 per hour. Fargate Spot reduces that by up to 70% — the same pod costs around $0.015 per hour on Spot. Fargate Spot pods can be interrupted with a two-minute warning when capacity is reclaimed.

The comparison to EC2 nodes depends heavily on utilization. A t3.large (2 vCPU, 8 GB) costs around $0.083 per hour on-demand and can run several pods simultaneously. If that node runs at 70% utilization, the effective cost per workload is lower than Fargate per-pod pricing. At 20% utilization — typical for dev environments and variable batch loads — Fargate is cheaper because you’re not paying for idle capacity.

The break-even point for most workloads is around 40-50% sustained utilization. Below that, Fargate wins. Above it, managed nodes win. For services that run 24/7 at steady throughput, Savings Plans and Reserved Instances on EC2 node groups cut costs significantly compared to either on-demand Fargate or on-demand EC2.

Networking: Each Pod Gets Its Own ENI

Fargate networking differs from how the VPC CNI works on EC2 nodes. On EC2, multiple pods share the node’s ENIs. On Fargate, each pod gets its own dedicated ENI with its own VPC IP address.

This means each Fargate pod is directly addressable within the VPC — no host-level NAT, no shared ENI limit to worry about. But it also means you need enough IP addresses in your Fargate subnets to accommodate the number of concurrent pods. A /24 subnet (251 usable IPs) can support up to 251 Fargate pods running simultaneously in that subnet. For clusters with variable load spikes, size subnets generously.

Fargate pods must run in private subnets. They don’t support host networking (hostNetwork: true). If pods need internet access for pulling images or calling external APIs, the subnet needs a NAT Gateway route. The ENI and subnet architecture is explored further in the EKS VPC CNI deep dive.

For security groups, Fargate pods use the security groups configured in the Fargate profile’s pod execution role — not node-level security groups. You can further restrict what the pod can access by scoping those security groups tightly.

Workload IAM: IRSA Is Required

EKS Pod Identity doesn’t work with Fargate pods. For Fargate workloads that need AWS permissions, IRSA (IAM Roles for Service Accounts) is the only mechanism.

Set up IRSA for a Fargate batch job:

# Associate the OIDC provider with your cluster (one-time)
eksctl utils associate-iam-oidc-provider \
  --cluster my-cluster \
  --approve

# Create a service account with an IAM role for S3 access
eksctl create iamserviceaccount \
  --name batch-worker \
  --namespace batch \
  --cluster my-cluster \
  --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
  --approve

The service account gets annotated with the IAM role ARN. Pods using that service account receive temporary credentials via the AWS SDK credential chain. The IRSA trust policy embeds the cluster’s OIDC provider URL — if you rebuild the cluster, the OIDC URL changes and you need to update the trust policies. This is one of the reasons EKS Pod Identity was introduced for EC2 workloads, but the limitation is fixed for Fargate in the sense that IRSA still works reliably — it just requires the OIDC setup step. The full mechanics of trust policies and cross-service IAM are covered in the AWS IAM roles and policies guide.

Logging on Fargate

Fargate pods can’t run DaemonSets, so the typical approach of a Fluent Bit or Fluentd DaemonSet for log collection doesn’t work. Instead, Fargate has a built-in log router that sidecars onto every pod automatically.

Enable it by creating a ConfigMap in the aws-observability namespace:

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-fargate-logging
  namespace: aws-observability
data:
  flb_log_cw: "true"
  output.conf: |
    [OUTPUT]
        Name cloudwatch_logs
        Match *
        region us-east-1
        log_group_name /aws/eks/my-cluster/fargate
        log_stream_prefix fargate-
        auto_create_group true

This routes all container stdout/stderr from Fargate pods to CloudWatch Logs automatically. You can also route to Kinesis Data Firehose for S3 archival or to a custom HTTP endpoint.

The aws-observability namespace must exist before the ConfigMap is created, and it must be included in a Fargate profile for the log router to activate:

# Create the namespace
kubectl create namespace aws-observability

# Ensure it's covered by a Fargate profile
eksctl create fargateprofile \
  --cluster my-cluster \
  --name system-pods \
  --namespace aws-observability

Without this, Fargate pods default to no log routing — stdout goes nowhere you can easily retrieve. Don’t skip it.

Ephemeral Storage

Fargate pods get 20 GB of ephemeral storage by default. That covers the container image layers and any data written to the container’s filesystem during runtime. For batch jobs that write large intermediate files, 20 GB may not be enough.

Increase it up to 200 GB:

spec:
  containers:
    - name: data-processor
      image: my-processor:latest
  ephemeralContainers: []
  overhead: {}
  # Fargate-specific annotation for storage
  annotations:
    eks.amazonaws.com/fargate-profile: batch-jobs

Set the storage request in the pod spec:

spec:
  containers:
    - name: data-processor
      resources:
        requests:
          ephemeral-storage: "50Gi"
        limits:
          ephemeral-storage: "50Gi"

Fargate rounds up to the nearest supported storage size and adds it to your hourly bill at $0.000111 per GB-hour beyond the included 20 GB. For a 50 GB request, the extra 30 GB adds about $0.003 per hour — negligible for most batch workloads but worth knowing it’s there.

Persistent storage on Fargate is limited to EFS. EBS volumes require a node-level attachment that Fargate can’t do. If your workload needs persistent block storage with ReadWriteOnce semantics, it doesn’t belong on Fargate.

What Fargate Can’t Do

Understanding the limitations saves you from designing around them after the fact.

No DaemonSets. Fargate has no concept of a node to pin a DaemonSet to. Anything that deploys as a DaemonSet — node-level monitoring agents, security scanners, log collectors, CSI drivers — has to run on EC2 nodes or be replaced with a Fargate-compatible alternative. The built-in log router handles the logging use case; the rest requires a mixed-node strategy.

No GPU workloads. Fargate doesn’t offer GPU instance types. Machine learning inference or training jobs that need NVIDIA GPUs have to run on managed node groups with the appropriate instance type.

No privileged containers. Pods requiring privileged: true in their security context won’t schedule on Fargate. This includes some monitoring agents and certain CNI-level tools. Pod Security Standards at the restricted level are a natural fit for Fargate since privileged containers are prohibited anyway.

No host networking. hostNetwork: true isn’t supported. Pods that need direct access to the host’s network namespace — certain network scanners, eBPF-based observability tools — can’t run on Fargate.

IRSA only for workload IAM. As noted, EKS Pod Identity isn’t supported. If you’re standardizing on Pod Identity for your EC2 workloads (covered in the EKS RBAC and security guide), Fargate workloads need IRSA in parallel.

Running CoreDNS on Fargate

CoreDNS normally runs on EC2 nodes. If you want a fully nodeless cluster (Fargate only), CoreDNS needs a Fargate profile:

# Create Fargate profile for kube-system
eksctl create fargateprofile \
  --cluster my-cluster \
  --name system \
  --namespace kube-system

# Patch CoreDNS to remove the EC2 node annotation
kubectl patch deployment coredns \
  -n kube-system \
  --type json \
  -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'

Without removing the annotation, CoreDNS prefers EC2 scheduling. After patching, it reschedules on Fargate. This takes a few minutes and briefly interrupts DNS resolution in the cluster — do it during a maintenance window for production clusters.

When to Use Fargate

Fargate is the right choice for:

Batch and job workloads with irregular schedules. Kubernetes Jobs and CronJobs on Fargate scale to zero between runs — you pay only for active compute. A nightly data pipeline that runs for 30 minutes costs 30 minutes of Fargate pricing, not 24 hours of idle EC2.

Dev and staging environments where teams run workloads intermittently. No idle node costs during off-hours.

Variable traffic with wide swings. If your traffic pattern spikes 10x on certain days and drops to near-zero otherwise, Fargate avoids paying for peak-sized nodes during valleys. Fargate Spot amplifies this further.

Security-sensitive workloads that benefit from kernel-level isolation. Each Fargate pod runs on its own micro-VM — there’s no shared kernel with other pods the way there is on a multi-tenant EC2 node.

Stay on managed nodes for stateful workloads needing EBS, any workload requiring DaemonSets, GPU inference, and any service running at sustained high utilization where EC2 Reserved Instances or Savings Plans make the cost math work in favor of EC2. Mixing both in one cluster — Fargate profiles for appropriate workloads, managed node groups for everything else — is the most common production pattern and the one the EKS getting started guide sets up as its recommended baseline.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus