EKS Monitoring Guide: CloudWatch, Prometheus & Grafana

EKS does not ship with monitoring. A pod can crash-loop for hours before anything in the AWS Console turns red. This page explains how to choose between CloudWatch Container Insights, Prometheus, Grafana, and Amazon Managed Service for Prometheus, and how to get useful alerts in place before something breaks.

Your monitoring options, and when to use each

Four main options exist for EKS monitoring:

  • CloudWatch Container Insights: the AWS-native path. Install the Amazon CloudWatch Observability EKS add-on and you get infrastructure metrics, Kubernetes state metrics, and log forwarding out of the box. Good fit if your team already works in the AWS Console and wants minimal operational overhead.
  • Prometheus and Grafana (self-managed): the most widely used open-source monitoring stack for Kubernetes, installed via the kube-prometheus-stack Helm chart. More powerful query language (PromQL), richer dashboards, and better support for application-level metrics. Requires you to run and maintain the stack yourself.
  • Amazon Managed Service for Prometheus (AMP) with Grafana: Prometheus-compatible managed backend. Your Prometheus or the AWS managed collector scrapes your cluster and sends metrics to AMP; Grafana queries AMP for dashboards. Best when you want Prometheus without managing its storage and availability.
  • Hybrid: CloudWatch for infrastructure and audit logs, Prometheus or AMP for application metrics and dashboards. Common in production environments where teams want both deep Kubernetes metrics and AWS-native compliance tooling.
New to EKS monitoring?

Start with the CloudWatch Observability add-on. One command installs everything you need to get basic visibility. You can layer in Prometheus or AMP later once you know what you actually need to measure.

For a deeper look at the CloudWatch side of this, see the How to Monitor Amazon EKS with CloudWatch guide. This page focuses on comparing your options and implementing them.

What EKS monitoring actually means

Analogy

Think of your EKS cluster as a factory floor. Nodes are the machines. Pods are the products being assembled. The network is the conveyor belt. Monitoring means watching whether machines are overheating (high CPU), whether the assembly line has stalled (pending pods), whether products are coming out defective (crash loops), and keeping a security log of who accessed what and when (audit logs). A factory with no gauges and no cameras is not safer because things look quiet. It is just blind.

EKS monitoring has four distinct concerns that are easy to blur together:

  • Infrastructure metrics: CPU, memory, disk, and network at the node level. This tells you whether your EC2 instances are under pressure. The CloudWatch agent or Prometheus node exporter collects these.
  • Kubernetes state metrics: pod phases (Running, Pending, Failed), deployment replica counts, job completions. These come from the Kubernetes API via kube-state-metrics, not from the nodes themselves.
  • Logs: the stdout/stderr output from containers, plus EKS control plane logs (API server, scheduler, controller manager, audit). Fluent Bit forwards container logs; control plane logs are enabled separately per cluster.
  • Alerts and dashboards: rules that fire when something crosses a threshold, visualized in CloudWatch or Grafana.

These four concerns are separate. You can have excellent infrastructure metrics but no visibility into whether your deployments are degraded. You can have all your logs and no alerts. A useful monitoring setup covers all four.

How EKS monitoring works

Understanding the data flow makes the architecture less confusing:

  1. Sources: pods, nodes, and the EKS control plane generate metrics and logs continuously.
  2. Agents and collectors: the CloudWatch agent (or Prometheus node exporter + kube-state-metrics) runs on each node and scrapes or receives these signals. Fluent Bit collects logs.
  3. Storage: metrics flow to CloudWatch (custom metrics namespace) or to Prometheus / AMP. Logs flow to CloudWatch Logs log groups.
  4. Query and dashboards: CloudWatch console, CloudWatch Dashboards, or Grafana query the stored data.
  5. Alerts: CloudWatch Alarms or Prometheus Alertmanager evaluate rules and send notifications to SNS, PagerDuty, Slack, or email.
Pods / Nodes / Control Plane
       |
       | (metrics)              (logs)
       v                          v
CloudWatch Agent           Fluent Bit DaemonSet
or Prometheus scraper             |
       |                          v
       v                    CloudWatch Logs
CloudWatch Metrics               |
or AMP (Prometheus storage)      v
       |                   CloudWatch Insights
       v                   (query and search)
Dashboards + Alarms
(CloudWatch Console or Grafana)
The AWS-native shortcut

The Amazon CloudWatch Observability EKS add-on installs the CloudWatch agent and Fluent Bit together. Steps 2 and 3 above are handled automatically when you run a single aws eks create-addon command.

Which setup should you choose?

OptionBest forProsTrade-offs
CloudWatch only (EKS add-on)Teams that want AWS-native tooling with minimal ops overheadOne add-on installs everything; integrates with IAM, CloudTrail, and CloudWatch Alarms; enables Application SignalsCloudWatch custom metrics cost money per metric per month; limited PromQL-style querying; fewer community dashboards
Self-managed Prometheus + GrafanaTeams with Kubernetes expertise who want full controlFree, highly configurable, huge ecosystem, excellent application metrics supportYou run and scale Prometheus and Grafana yourself; storage is local to the cluster by default
AMP + GrafanaTeams that want Prometheus without managing its storage and HAManaged Prometheus backend; scales automatically; integrates with Amazon Managed GrafanaAdditional per-metric-sample cost; more moving parts than CloudWatch-only
Hybrid (CloudWatch + Prometheus or AMP)Production clusters needing both AWS-native compliance and deep application metricsBest of both: CloudWatch for audit logs and infra, Prometheus for app metricsMost complex to set up and maintain; two billing surfaces

For a comparison of broader EKS cluster management models, see Managed vs Self-Managed Kubernetes in AWS.

Quick checks with kubectl top

The fastest way to check current resource usage is kubectl top. This is useful for ad-hoc spot checks, not for monitoring.

# Check node resource usage
kubectl top nodes

# NAME                           CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
# ip-10-0-1-100.ec2.internal    245m         12%    1824Mi          47%
# ip-10-0-1-101.ec2.internal    412m         20%    2134Mi          55%

# Check pod resource usage in a namespace
kubectl top pods -n production --sort-by=memory

kubectl top depends on Metrics Server being installed (see the Horizontal Pod Autoscaling guide for that setup).

What it is good for: seeing which pods or nodes are consuming the most resources right now. Useful when debugging an active issue.

What kubectl top cannot do

kubectl top shows a single point-in-time snapshot. It has no history, no alerting, and no way to see whether current usage is normal or anomalous. Teams that rely on it as their main monitoring tool routinely miss slow-building problems like steady memory growth or gradual node pressure.

CloudWatch Container Insights on EKS

Container Insights collects metrics at the cluster, node, namespace, pod, and container level and makes them visible in the CloudWatch console under Insights > Container Insights.

What it collects:

  • Node CPU and memory utilization
  • Pod CPU, memory, network, and disk usage
  • Number of running pods per namespace
  • Container restart counts
  • EKS control plane metrics via CloudWatch Logs

Installing via the EKS add-on (recommended)

The Amazon CloudWatch Observability EKS add-on installs the CloudWatch agent and Fluent Bit as DaemonSets in a single step. It also enables CloudWatch Application Signals for automatic application performance monitoring:

aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name amazon-cloudwatch-observability \
  --region eu-west-1

IAM permissions

The add-on needs an IAM role with CloudWatchAgentServerPolicy. The preferred approach is EKS Pod Identity, which is the current AWS-recommended credential path for pod-level IAM access. IAM Roles for Service Accounts (IRSA) also works and is still widely used on existing clusters.

# Create the role and attach the policy
aws iam create-role \
  --role-name EKSCloudWatchObservabilityRole \
  --assume-role-policy-document file://eks-pod-identity-trust-policy.json

aws iam attach-role-policy \
  --role-name EKSCloudWatchObservabilityRole \
  --policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

# Associate the role with the add-on service account
aws eks create-pod-identity-association \
  --cluster-name my-cluster \
  --namespace amazon-cloudwatch \
  --service-account cloudwatch-agent \
  --role-arn arn:aws:iam::123456789012:role/EKSCloudWatchObservabilityRole

Metrics appear in the ContainerInsights CloudWatch namespace within a few minutes.

Application Signals

The add-on also enables CloudWatch Application Signals, which adds automatic application-level performance monitoring (request traces, error rates, latency histograms) for supported runtimes without code changes. It is off by default but worth enabling if you want APM without a separate tool.

Cost note

Container Insights charges per custom metric published and per GB of logs ingested. The more nodes and pods you have, the higher the bill. Set a CloudWatch Logs retention policy on all Container Insights log groups from day one, before the logs accumulate.

Prometheus and Grafana on EKS

How Prometheus works

Prometheus works like a polling reporter rather than a passive listener. Every 15–30 seconds it visits each pod and node (via HTTP endpoints) and asks “what are your numbers right now?” It records those numbers in a time-series database. Grafana then reads that database to draw charts. Alertmanager watches the database and fires alerts when rules are triggered.

When self-managed Prometheus makes sense:

  • Your team already knows PromQL
  • You need application-level metrics exposed by your services (request rate, error rate, latency percentiles)
  • You want community dashboards — Grafana has thousands of pre-built Kubernetes dashboards
  • Cost at scale is a concern and you can carry the operational overhead

Install via the kube-prometheus-stack Helm chart, which bundles Prometheus, Grafana, Alertmanager, and pre-built Kubernetes dashboards. See the Helm Package Manager guide if you are new to Helm.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set grafana.adminPassword=change-this-password

Access Grafana locally via port forwarding during initial setup:

kubectl port-forward service/prometheus-stack-grafana 3000:80 -n monitoring

Open http://localhost:3000, log in with admin and the password you set. The default dashboards show cluster-wide CPU and memory usage, pod restart rates, and network traffic.

The storage problem

The default Prometheus server stores all metrics on a local PersistentVolume with no replication. If the pod restarts or the disk fills up, you lose metric history. This is fine for development clusters. For production, either configure persistent storage with a generous retention window or use AMP to offload storage entirely.

Amazon Managed Service for Prometheus (AMP)

AMP is a fully managed Prometheus-compatible backend. You send metrics to it the same way you would send them to a self-managed Prometheus server, but AWS handles storage, scaling, and high availability.

When AMP is better than self-managed Prometheus:

  • You need long-term metric retention beyond what a local PersistentVolume can hold
  • You have multiple clusters and want to aggregate metrics in one place
  • You do not want to manage Prometheus availability and storage yourself
  • You are already using Amazon Managed Grafana and want a tightly integrated stack

Two ways to get metrics into AMP

Option 1: Prometheus remote_write. If you already have Prometheus running, configure it to write metrics to AMP:

# prometheus-values.yaml
prometheus:
  prometheusSpec:
    remoteWrite:
    - url: https://aps-workspaces.eu-west-1.amazonaws.com/workspaces/ws-xxxxx/api/v1/remote_write
      sigv4:
        region: eu-west-1
        roleArn: arn:aws:iam::123456789012:role/prometheus-remote-write-role

Option 2: AWS managed scraper. AMP can deploy a managed scraper into your cluster via the AWS API. You do not need to install Prometheus at all. AWS provisions the scraper, discovers your pods, and sends metrics directly to your AMP workspace.

aws amp create-scraper \
  --source eksConfiguration="{clusterArn='arn:aws:eks:eu-west-1:123456789012:cluster/my-cluster',subnetIds=['subnet-abc123']}" \
  --destination ampConfiguration="{workspaceArn='arn:aws:aps:eu-west-1:123456789012:workspace/ws-xxxxx'}" \
  --scrape-configuration configurationBlob=$(cat scrape-config.yaml | base64)

Grafana with AMP: use either Amazon Managed Grafana or a self-hosted Grafana instance. Both connect to AMP using the Prometheus data source plugin with SigV4 authentication, configured via IRSA or EKS Pod Identity.

When to switch to AMP

If your Prometheus pod has restarted and taken metric history with it, or if you are spending real time managing Prometheus storage, that is the signal to move to AMP. The managed scraper option lets you remove the in-cluster Prometheus entirely once you do.

Logs and control plane visibility

Metrics tell you something is wrong. Logs tell you why. The two are not interchangeable.

Pod and container logs are written to stdout/stderr by containers and stored as files on each node. Fluent Bit, running as a DaemonSet, reads those files and forwards them to CloudWatch Logs. The CloudWatch Observability add-on installs Fluent Bit automatically. For more on how log forwarding works, see Logging in Kubernetes.

CloudWatch Logs organizes container logs by:

  • /aws/containerinsights/CLUSTER_NAME/application: container stdout/stderr
  • /aws/containerinsights/CLUSTER_NAME/host: node system logs
  • /aws/containerinsights/CLUSTER_NAME/dataplane: kubelet and containerd logs

EKS control plane logs are separate from container logs. They cover the API server, scheduler, controller manager, authenticator, and audit trail. Enable them per cluster:

aws eks update-cluster-config \
  --name my-cluster \
  --region eu-west-1 \
  --logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'

Audit logs record every API call made to the cluster: who made it, which resource they accessed, and whether it was allowed. They are essential for security investigations. Query them with CloudWatch Logs Insights:

fields @timestamp, @message
| filter @logStream like /kube-apiserver-audit/
| filter requestURI like /secrets/
| sort @timestamp desc
| limit 100

For guidance on querying and searching logs, see CloudWatch Logs.

Set a retention policy before enabling audit logs

Audit logs on a busy cluster can generate several gigabytes per day. CloudWatch Logs groups never expire by default. If you enable control plane logging without a retention policy first, costs accumulate silently and can run high before anyone notices. Set the retention policy before the first logs arrive, not after.

What to alert on first

Start with a small set of high-signal alerts. The goal is to catch real problems quickly, not to alert on everything that could theoretically go wrong.

SignalStarter thresholdWhy it matters
Pod restart countMore than 3 restarts in 10 minutesIndicates crash loops from application errors or resource exhaustion. See EKS CrashLoopBackOff Explained
Pending podsAny pod Pending for more than 5 minutesScheduling failure: insufficient capacity or a node selector mismatch
Node memory utilizationAbove 90% for 5 minutesSustained memory pressure leads to OOMKill events without warning
Node CPU utilizationAbove 85% for 5 minutesCPU throttling affects all pods on that node, not just the busiest one
Node not readyAny node in NotReady state for 2 minutesNode has lost contact with the control plane or has a critical problem
API server latency (P99)Above 1 secondControl plane degradation slows deployments, scaling, and health checks
Running pod countBelow minimum replicas for 10 minutesCatches deployment failures that restart counts alone would miss

These are starter thresholds for a typical cluster. Tune them once you have baseline data. A batch-processing cluster running near capacity will need looser CPU thresholds than a lightly loaded API cluster.

Start with five alerts

Pick five of the above and get them working well before adding more. Alerts you trust are more useful than twenty alerts that people have learned to ignore. Pod restarts, pending pods, and node memory are the highest-signal starting point for most clusters.

For step-by-step instructions on creating these in CloudWatch, see Creating CloudWatch Alarms.

Which setup fits your situation

Small development cluster (1 to 5 nodes)

The CloudWatch Observability add-on with default settings is enough. Install it, enable basic alarms on pod restarts and node memory, and move on. Adding a full Prometheus stack to a cluster used by one or two developers adds operational overhead with little benefit.

Growing production cluster (5 to 30 nodes)

Add kube-prometheus-stack for application-level metrics, especially if your services expose Prometheus endpoints. Use CloudWatch for audit logs and compliance. Set up Grafana dashboards per service team so ownership is clear from the start.

Multi-team or multi-cluster setup

AMP with the managed scraper is worth the extra setup at this scale. A single Grafana workspace can query multiple AMP workspaces, giving you a unified view across clusters. Each team can have namespace-scoped dashboards with Grafana RBAC controlling what they can see.

Compliance or security-sensitive environment

Enable all five EKS control plane log types, including audit logs, and set a minimum retention of 90 days. Define who is responsible for reviewing them and under what conditions. Monitoring and access control overlap here, so read this page alongside Securing EKS Clusters for the full picture.

Common mistakes

  1. Relying only on kubectl top. kubectl top shows what is happening right now. It has no history and no alerting. Teams that use it as their main monitoring tool miss slow-building problems like gradual memory growth or a node that has been under pressure for hours.
  2. No log retention policy. CloudWatch Logs groups do not expire by default. Container logs and control plane logs accumulate indefinitely. Set a retention policy on every log group when you create it. 14 to 30 days is typical for application logs; longer for audit logs.
  3. Noisy alerts that get ignored. Alerting on CPU above 50% on a normally loaded cluster creates constant noise. When alerts fire constantly, engineers learn to dismiss them. Alert on anomalies (crash loops, sustained spikes, scheduling failures) and calibrate thresholds to your cluster’s actual baseline.
  4. Monitoring infrastructure but not workloads. Node CPU and memory tell you about capacity. They do not tell you whether your application is returning errors, whether latency has increased, or whether a recent deploy degraded throughput. Add application-level metrics once infrastructure monitoring is stable.
  5. No ownership for dashboards and alerts. Dashboards and alert rules go stale when nobody owns them. Assign ownership per service team or namespace. If an alert fires and nobody knows what it means or what to do, it was not set up with enough context.
  6. Enabling all control plane log types without a plan. Audit logs are valuable but can be expensive on a busy cluster. Decide on retention and cost limits before enabling them. The danger is not enabling them — it is enabling them without a retention policy and then being surprised by the bill.

Frequently asked questions

Do I need Prometheus if I already use CloudWatch?

Not necessarily. CloudWatch Container Insights covers infrastructure and Kubernetes state metrics well and requires less setup. Prometheus becomes worthwhile when you need application-level metrics (request rate, error rate, latency), multi-cluster aggregation, or a richer query language (PromQL). Many teams start with CloudWatch and add Prometheus later as their needs grow.

What is the difference between Container Insights and Amazon Managed Service for Prometheus?

Container Insights is a CloudWatch feature that collects and displays Kubernetes infrastructure metrics (pod CPU, node memory, running pod counts). Amazon Managed Service for Prometheus (AMP) is a fully managed Prometheus backend you send metrics to. They solve different problems: Container Insights is AWS-native and needs minimal setup, while AMP is for teams already using Prometheus who want managed storage and high availability without running their own server.

What should I monitor first in EKS?

Start with pod restart counts, pending pods, and node memory utilization. These three signals catch most early problems: crash loops (restarts), scheduling failures (pending pods), and resource exhaustion (memory). Once those are in place, add node CPU and API server latency.

How do I monitor EKS logs?

Pod and container logs are written to stdout/stderr and can be forwarded to CloudWatch Logs using Fluent Bit, which runs as a DaemonSet on each node. The Amazon CloudWatch Observability EKS add-on installs Fluent Bit automatically alongside the CloudWatch agent. For EKS control plane logs (API server, audit, scheduler), enable them separately per cluster using the AWS CLI or console.

Is self-managed Prometheus still worth it?

It depends on your scale and team. Self-managed Prometheus (via the kube-prometheus-stack Helm chart) is free, highly configurable, and sufficient for a single cluster. At scale with multiple clusters, long retention needs, or limited operational bandwidth, Amazon Managed Service for Prometheus removes the burden of running and scaling Prometheus yourself. Both paths are valid.

Last verified: 4 April 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.