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-stackHelm 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.
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
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:
- Sources: pods, nodes, and the EKS control plane generate metrics and logs continuously.
- 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.
- Storage: metrics flow to CloudWatch (custom metrics namespace) or to Prometheus / AMP. Logs flow to CloudWatch Logs log groups.
- Query and dashboards: CloudWatch console, CloudWatch Dashboards, or Grafana query the stored data.
- 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 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?
| Option | Best for | Pros | Trade-offs |
|---|---|---|---|
| CloudWatch only (EKS add-on) | Teams that want AWS-native tooling with minimal ops overhead | One add-on installs everything; integrates with IAM, CloudTrail, and CloudWatch Alarms; enables Application Signals | CloudWatch custom metrics cost money per metric per month; limited PromQL-style querying; fewer community dashboards |
| Self-managed Prometheus + Grafana | Teams with Kubernetes expertise who want full control | Free, highly configurable, huge ecosystem, excellent application metrics support | You run and scale Prometheus and Grafana yourself; storage is local to the cluster by default |
| AMP + Grafana | Teams that want Prometheus without managing its storage and HA | Managed Prometheus backend; scales automatically; integrates with Amazon Managed Grafana | Additional 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 metrics | Best of both: CloudWatch for audit logs and infra, Prometheus for app metrics | Most 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=memorykubectl 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.
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-1IAM 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/EKSCloudWatchObservabilityRoleMetrics appear in the ContainerInsights CloudWatch namespace within a few minutes.
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.
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
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-passwordAccess Grafana locally via port forwarding during initial setup:
kubectl port-forward service/prometheus-stack-grafana 3000:80 -n monitoringOpen 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 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-roleOption 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.
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 100For guidance on querying and searching logs, see CloudWatch 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.
| Signal | Starter threshold | Why it matters |
|---|---|---|
| Pod restart count | More than 3 restarts in 10 minutes | Indicates crash loops from application errors or resource exhaustion. See EKS CrashLoopBackOff Explained |
| Pending pods | Any pod Pending for more than 5 minutes | Scheduling failure: insufficient capacity or a node selector mismatch |
| Node memory utilization | Above 90% for 5 minutes | Sustained memory pressure leads to OOMKill events without warning |
| Node CPU utilization | Above 85% for 5 minutes | CPU throttling affects all pods on that node, not just the busiest one |
| Node not ready | Any node in NotReady state for 2 minutes | Node has lost contact with the control plane or has a critical problem |
| API server latency (P99) | Above 1 second | Control plane degradation slows deployments, scaling, and health checks |
| Running pod count | Below minimum replicas for 10 minutes | Catches 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.
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
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.
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.
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.
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
Summary
- EKS monitoring has four parts: infrastructure metrics, Kubernetes state metrics, logs, and alerts. Each requires different tooling.
- The Amazon CloudWatch Observability EKS add-on installs the CloudWatch agent and Fluent Bit in one step. It is the fastest AWS-native starting point.
- Prometheus with kube-prometheus-stack adds PromQL, richer dashboards, and application-level metrics but requires more operational work.
- AMP removes the storage and HA burden from self-managed Prometheus, with a managed scraper option that eliminates in-cluster Prometheus entirely.
kubectl topis a quick point-in-time check, not a monitoring solution. It needs Metrics Server and shows no history.- Enable EKS control plane audit logs in production, but set a CloudWatch Logs retention policy before the first logs arrive.
- Start with five high-signal alerts: pod restarts, pending pods, node memory, node not ready, and API server latency.
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.