Self awareness: the Downward API
Your app runs in a Pod, but it does not automatically know which Pod. What is my name? Which node am I on? How much memory am I allowed? The Downward API hands these facts "down" from Kubernetes into the container, as environment variables or as files. No extra code, no calls to the Kubernetes API.
Why an app wants to know about itself
Think of a name badge handed to a new employee at the door: name, department, desk number. They did not have to go and ask. The Downward API is that badge.
- Logs and traces: put the Pod name in every log line, so you know which of 5 copies failed.
- Sizing: tune memory or thread counts to the container's actual limits.
- Identity: use the Pod name as a unique worker ID, or the node name for "rack awareness".
- Behaviour by label: read a
versionortracklabel, e.g. to tag canary metrics (chapter 15).
What you can ask for
| Field | Gives | As env | As file |
|---|---|---|---|
metadata.name | Pod name | ✓ | ✓ |
metadata.namespace | Namespace | ✓ | ✓ |
metadata.uid | Unique Pod ID | ✓ | ✓ |
metadata.labels['app'] | One label | ✓ | ✓ |
metadata.labels, metadata.annotations | All labels / annotations | — | ✓ (and kept up to date) |
status.podIP, status.hostIP | Pod IP, node IP | ✓ | — |
spec.nodeName | Node name | ✓ | — |
spec.serviceAccountName | ServiceAccount (chapter 43) | ✓ | — |
limits.cpu, limits.memory, requests.* | The container's own resources (chapter 22) | ✓ | ✓ |
The first group uses fieldRef; the resource numbers use resourceFieldRef.
As environment variables
Same env list as chapter 16, but instead of value: you say where the value comes from. Click the parts:
containers:
- name: posts
image: micro-posts:1.0
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
resources:
limits:
memory: 512Mi
fieldRef
Points at a field of the Pod object. metadata.name becomes something like posts-depl-7d9f8c6b5d-4kq2p.
resourceFieldRef
Reads this container's request or limit. divisor: 1Mi turns 512Mi into the plain number 512. Without a limit set, you get the node's total capacity instead.
// posts/index.js
const log = (msg) => console.log(`[${process.env.POD_NAME} on ${process.env.NODE_NAME}] ${msg}`);
log('Posts listening on 4000');
// [posts-depl-7d9f8c6b5d-4kq2p on node-1] Posts listening on 4000
As files
spec:
containers:
- name: posts
image: micro-posts:1.0
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: labels
fieldRef: { fieldPath: metadata.labels }
- path: annotations
fieldRef: { fieldPath: metadata.annotations }
- path: name
fieldRef: { fieldPath: metadata.name }$ kubectl exec deployment/posts-depl -- cat /etc/podinfo/labels
app="posts"
pod-template-hash="7d9f8c6b5d"
version="v2"
$ kubectl exec deployment/posts-depl -- cat /etc/podinfo/name
posts-depl-7d9f8c6b5d-4kq2pThe big difference: files stay up to date, environment variables do not. Step through:
$ kubectl label pod posts-depl-7d9f8c6b5d-4kq2p tier=goldYou add a label while the Pod is running.
env: (no change: env vars were copied when the container started) file: /etc/podinfo/labels → still the old content for a momentThe kubelet refreshes Downward API files periodically.
$ kubectl exec … -- cat /etc/podinfo/labels app="posts" tier="gold" ← appears without a restart version="v2"Shortly after, the file shows the new label. Use files for anything that can change while the Pod runs: labels and annotations.
Real example: sizing Node's memory
Node.js picks its maximum heap size on its own, and it does not always look at the container's limit. If the heap grows past the limit, the container is OOMKilled (chapter 22). Hand Node the real limit and keep some room for everything outside the heap:
env:
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
command: ["sh", "-c", "exec node --max-old-space-size=$((MEMORY_LIMIT_MB * 3 / 4)) index.js"]
Change the limit in YAML and the heap follows automatically. No second number to keep in sync. (exec keeps Node as the main process, so it still receives SIGTERM: chapter 14.)
What it cannot do
- It only describes the Pod itself. Other Pods, Services or the cluster are off limits; for that an app must call the Kubernetes API with permission (chapter 43).
- Env vars are fixed at start. Use the file form for values that change.
- It reads Kubernetes data only; your own settings belong in ConfigMaps (chapter 18).
- Put the Pod name into every log line
The name never changes for a Pod, so an env var is simplest.
- React when someone adds an annotation to the running Pod
Only the volume is refreshed while the Pod runs, and all annotations are only available as a file anyway.
- Get the IPs of the other comments Pods
The Downward API only knows about the Pod itself. Use a headless Service (chapter 26).
Recap
- The Downward API gives a container facts about its own Pod, with no API calls.
fieldRef: name, namespace, IP, node, labels, annotations.resourceFieldRef: requests and limits.- As env vars: simple, fixed at start. As a
downwardAPIvolume: files, and labels/annotations update live. - Classic uses: Pod name in logs, Node heap size from the memory limit.
Quick check
MEMORY_LIMIT_MB from limits.memory, but the container has no memory limit. What value arrives?Show answer
The node's allocatable memory, which is far too big to size a heap from. Always set a memory limit when you read it this way.
Downward API: common questions
How does a Pod know its own name in Kubernetes?
Use the Downward API: an env var with valueFrom.fieldRef.fieldPath set to metadata.name, or a downwardAPI volume.