kubectl top: CPU and memory right now
Where kubectl top gets its numbers, how to read millicores, and why the command doesn't work without metrics-server.
Why
A Pod is lagging, a node is under load, and the first thing you want to
know is who's eating CPU and memory right now. kubectl top gives the
fastest answer. It's also the first thing that trips people up: on a
fresh cluster the command often fails, and it's worth understanding why.
Concepts
kubectl is the command-line tool for managing Kubernetes. A Pod is the smallest unit you run, and one or more containers run inside it. A node is a machine in the cluster that runs Pods.
kubelet is the Kubernetes agent on a node that runs containers and knows how many resources they're using. metrics-server is the component that collects current CPU and memory metrics from every kubelet and serves them through the Kubernetes API.
The kubectl top command shows current CPU and memory consumption by Pod
and by node. It's a snapshot of the current moment: there are no charts,
no history, no aggregation here. But it's the fastest way to answer "who's
eating memory" or "how loaded is this node".
How it works
kubectl itself doesn't compute anything. It makes a GET request to the
Kubernetes API at /apis/metrics.k8s.io/v1beta1/ and formats the
response. metrics-server sits behind that path.
kubectl top
└─> Kubernetes API /apis/metrics.k8s.io/v1beta1/
└─> metrics-server every 15 s
└─> kubelet /metrics/resource
metrics-server polls every node's kubelet every 15 seconds through the
/metrics/resource endpoint. kubelet returns consumption by container,
Pod and node. The interval is set by the --metric-resolution flag: the
official manifest sets it to 15s, and without the flag metrics-server
polls every 60 seconds.
Without metrics-server, kubectl top doesn't work at all:
kubectl top pod
error: Metrics API not available
metrics-server isn't part of Kubernetes, and many clusters don't have it
installed by default. Some distributions and managed providers install
it out of the box, others leave it to the administrator. kind, for
example, doesn't have it. The error Metrics API not available on a
fresh cluster most often means exactly this, not a broken cluster. You
can confirm it like this:
kubectl get apiservice v1beta1.metrics.k8s.io
Error from server (NotFound): apiservices.apiregistration.k8s.io "v1beta1.metrics.k8s.io" not found
metrics-server is installed as a separate manifest or Helm chart from the kubernetes-sigs/metrics-server repository. It's best to pin the version explicitly:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml
Main commands
# All Pods in the current namespace.
kubectl top pod
# Across the whole cluster.
kubectl top pod -A
# Sorting.
kubectl top pod --sort-by=cpu
kubectl top pod --sort-by=memory
# Per-container breakdown within a Pod.
kubectl top pod --containers
# Nodes.
kubectl top node
kubectl top node --sort-by=cpu
Empty output or an error right after installing metrics-server doesn't mean it's broken. It takes 30-60 seconds to collect the first reading.
The --containers flag matters for Pods with a sidecar. A sidecar is a
helper container alongside the main one. With this flag you see each
container's consumption separately; without it, you get the Pod's total.
Units
CPU is measured in millicores. 100m is 0.1 of a core, 1000m is one
full core. The same unit is used in the requests and limits fields,
so the values are easy to compare without converting to percentages.
CPU in top is the average load over the last interval. The Linux kernel
only exposes a cumulative counter: how many seconds of CPU time a
container has used since it started. metrics-server takes two readings
and divides the difference by the time between them.
Memory is measured in bytes with a Mi or Gi suffix. 1Gi equals
1024 MiB: Kubernetes uses binary prefixes. top shows the working set.
The working set is the container memory the kernel can't reclaim under
memory pressure. It includes both process memory and files written to
tmpfs (for example, an emptyDir with medium: Memory).
The output of top node has a percentage column. That's the share of the
node's allocatable capacity. Allocatable is the amount of node resources
available for Pods. This percentage is what you use to decide whether
it's time to add a node.
Don't confuse consumption with requests and limits. requests is the
amount a Pod asked the scheduler for, limits is the ceiling. A Pod can
request 400Mi and use 1Mi, or request 64Mi and hold 120Mi in memory. In
top you only see the second number from each pair: what's actually in
use.
Direct access to the metrics API
# demo here is the namespace name, use your own
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/demo/pods"
kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
This is the same data top shows, but in raw JSON. Handy for scripts and
automation. If you have jq, add | jq to make the JSON readable.
If there's no metrics-server
Sometimes you can't or don't have time to install metrics-server: someone else's cluster, no permissions, you need an answer in five minutes. Then you can ask kubelet directly, through the same Kubernetes API. This is exactly the endpoint metrics-server polls:
kubectl get nodes
kubectl get --raw "/api/v1/nodes/<node name>/proxy/metrics/resource"
The response is in Prometheus text format: one line per metric, labels in curly braces, then the value and the measurement time in milliseconds. You need two metrics:
container_cpu_usage_seconds_total{container="app",namespace="demo",pod="web"} 12.48 1760270000000
container_memory_working_set_bytes{container="app",namespace="demo",pod="web"} 2.1004288e+07 1760270000000
Memory you can read right away: 2.1004288e+07 bytes is about 20Mi. CPU
is trickier. container_cpu_usage_seconds_total is that same cumulative
counter of seconds since the container started. A large value could mean
the container is loading the CPU right now, or that it's simply been
running for a long time. To get the load, take two readings with a pause
and compute the difference:
kubectl get --raw "/api/v1/nodes/<node name>/proxy/metrics/resource" | grep '^container_cpu_usage_seconds_total' | grep 'namespace="demo"'
If the counter grew by 2.5 over 10 seconds, the container was using 0.25
of a core on average, that is 250m. This is exactly how metrics-server
computes it too.
Have several nodes? Each node has its own endpoint, so poll them one
by one. You need nodes/proxy permissions, which a cluster administrator
has.
What top can't do
- Store history. Delete a Pod and its metrics are gone. For trends you need a time-series database, such as Prometheus.
- Catch short spikes. Readings come every 15 seconds, and a 5-second spike between readings is invisible to top.
- Break consumption down by process. Only a per-container total.
- Show network, IOPS (input/output operations per second), or the filesystem. That needs separate exporters.
top answers "what's happening right now", not "what happened last night".
Relation to HPA and requests
metrics-server is the foundation for HPA based on CPU and memory metrics.
HPA is a controller that changes the number of replicas based on load.
For a target like "60% CPU load", it takes the Pod's current consumption,
divides it by requests, and gets a percentage.
If the Pod's containers have no requests for that resource, the
percentage is undefined, and HPA won't scale on that target. A target in
absolute units (averageValue, for example 200m) works even without
requests.
Common mistakes
error: Metrics API not available. metrics-server isn't installed or didn't start. Checkkubectl get apiservice v1beta1.metrics.k8s.ioandkubectl get pods -n kube-system | grep metrics. On a cluster where nobody installed it, this is the expected response.- metrics-server exists but doesn't return data. A common cause is TLS:
kubelet has a self-signed certificate, and metrics-server doesn't trust
it. On test clusters (kind, minikube) this is fixed with the
--kubelet-insecure-tlsflag, which disables kubelet's certificate check. In production you need proper certificates. - Looking for a greedy Pod by
requestsandlimitsin the manifest. That's the order, not the consumption. - Comparing the raw
container_cpu_usage_seconds_totalcounter between containers as if it were load. It's a sum since startup; only the difference between two readings gives you load. - Budgeting resources from top. It's a snapshot, not an average. For planning, pull Prometheus metrics for at least a week.
In short
kubectl topshows instantaneous CPU and memory consumption, with no history or charts.- The numbers come from metrics-server through
/apis/metrics.k8s.io/v1beta1/, which gets them from kubelet's/metrics/resource. - metrics-server isn't part of Kubernetes, and
Metrics API not availableon a fresh cluster is a normal response, not a broken cluster. - CPU in millicores (
1000m= one core), memory inMi/Giwith binary prefixes, memory is the working set. - Without metrics-server, kubelet gives you the same data:
kubectl get --raw "/api/v1/nodes/<node>/proxy/metrics/resource". CPU there is cumulative, compute the load from two readings. - HPA based on load percentage divides consumption by
requestsand doesn't work without them.
What you'll need for the tasks
kubectl config current-context: confirm you're working in the lab cluster.kubectl top pod -n <ns>andkubectl get apiservice v1beta1.metrics.k8s.io: see and explain the error.kubectl get nodesandkubectl get --raw "/api/v1/nodes/<node>/proxy/metrics/resource".grepby metric name and namespace, two CPU readings with a pause.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 1 lab for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson k8s-observability-02-kubectl-top
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress