In this episode, we'll discuss the Downward API in Kubernetes. We'll learn how to expose Pod and container metadata to applications, use fieldRef and resourceFieldRef, and practical use cases.

In the previous episode, we learned about Environment Variables and how to configure applications using ConfigMaps, Secrets, and direct values. In episode 24, we'll discuss Downward API, a mechanism to expose Pod and container metadata to running applications.
Note: Here I'll be using a Kubernetes Cluster installed through K3s.
The Downward API allows containers to access information about themselves and their environment without calling the Kubernetes API server. This enables applications to be self-aware and adapt their behavior based on their runtime context.
Downward API is a mechanism that exposes Pod and container metadata to containers through environment variables or files. It provides a way for applications to discover information about their Kubernetes environment.
Think of Downward API like a mirror - it reflects information about the Pod back to the containers running inside it. Just as you look in a mirror to see yourself, containers use Downward API to see their own metadata.
Key characteristics of Downward API:
Downward API solves several important use cases:
Without Downward API, applications would need to call the Kubernetes API server or rely on external configuration, adding complexity and dependencies.
The Downward API exposes various Pod and container metadata.
Available via fieldRef:
metadata.name - Pod namemetadata.namespace - Pod namespacemetadata.uid - Pod unique IDmetadata.labels['<KEY>'] - Specific label valuemetadata.labels - All labels (volume only)metadata.annotations['<KEY>'] - Specific annotation valuemetadata.annotations - All annotations (volume only)Available via fieldRef:
status.podIP - Pod IP addressstatus.hostIP - Node IP addressspec.serviceAccountName - Service account namespec.nodeName - Node name where Pod runsAvailable via resourceFieldRef:
requests.cpu - CPU requestrequests.memory - Memory requestrequests.ephemeral-storage - Ephemeral storage requestlimits.cpu - CPU limitlimits.memory - Memory limitlimits.ephemeral-storage - Ephemeral storage limitExpose metadata as environment variables in containers.
Example: Basic Pod Information
apiVersion: v1
kind: Pod
metadata:
name: downward-env-pod
namespace: default
labels:
app: myapp
version: v1.0
tier: backend
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- |
echo "Pod Name: $POD_NAME"
echo "Pod Namespace: $POD_NAMESPACE"
echo "Pod IP: $POD_IP"
echo "Node Name: $NODE_NAME"
sleep 3600
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeNameExample: Accessing Labels
apiVersion: v1
kind: Pod
metadata:
name: labels-env-pod
labels:
app: myapp
version: v1.0
environment: production
spec:
containers:
- name: app
image: nginx:1.25
env:
- name: APP_LABEL
valueFrom:
fieldRef:
fieldPath: metadata.labels['app']
- name: VERSION_LABEL
valueFrom:
fieldRef:
fieldPath: metadata.labels['version']
- name: ENVIRONMENT_LABEL
valueFrom:
fieldRef:
fieldPath: metadata.labels['environment']Example: Accessing Annotations
apiVersion: v1
kind: Pod
metadata:
name: annotations-env-pod
annotations:
description: "Production web server"
owner: "platform-team"
version: "1.0.0"
spec:
containers:
- name: app
image: nginx:1.25
env:
- name: POD_DESCRIPTION
valueFrom:
fieldRef:
fieldPath: metadata.annotations['description']
- name: POD_OWNER
valueFrom:
fieldRef:
fieldPath: metadata.annotations['owner']
- name: POD_VERSION
valueFrom:
fieldRef:
fieldPath: metadata.annotations['version']Example: Container Resources
apiVersion: v1
kind: Pod
metadata:
name: resources-env-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
ephemeral-storage: "1Gi"
limits:
memory: "512Mi"
cpu: "500m"
ephemeral-storage: "2Gi"
env:
# Memory resources
- name: MEMORY_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.memory
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.memory
# CPU resources
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.cpu
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.cpu
# Storage resources
- name: STORAGE_REQUEST
valueFrom:
resourceFieldRef:
containerName: app
resource: requests.ephemeral-storage
- name: STORAGE_LIMIT
valueFrom:
resourceFieldRef:
containerName: app
resource: limits.ephemeral-storageConvert resource values to different units.
Example: Memory in Megabytes
apiVersion: v1
kind: Pod
metadata:
name: divisor-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
env:
# Memory in bytes (default)
- name: MEMORY_LIMIT_BYTES
valueFrom:
resourceFieldRef:
resource: limits.memory
# Memory in megabytes
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
# CPU in millicores (default)
- name: CPU_LIMIT_MILLICORES
valueFrom:
resourceFieldRef:
resource: limits.cpu
# CPU in cores
- name: CPU_LIMIT_CORES
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"Expose metadata as files in a volume.
Example: Basic Volume Mount
apiVersion: v1
kind: Pod
metadata:
name: downward-volume-pod
namespace: default
labels:
app: myapp
version: v1.0
annotations:
description: "Example application"
owner: "platform-team"
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- |
echo "=== Pod Metadata ==="
cat /etc/podinfo/name
cat /etc/podinfo/namespace
cat /etc/podinfo/labels
cat /etc/podinfo/annotations
sleep 3600
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "name"
fieldRef:
fieldPath: metadata.name
- path: "namespace"
fieldRef:
fieldPath: metadata.namespace
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotationsFiles created:
/etc/podinfo/name - Contains Pod name/etc/podinfo/namespace - Contains namespace/etc/podinfo/labels - Contains all labels/etc/podinfo/annotations - Contains all annotationsExample: Complete Metadata
apiVersion: v1
kind: Pod
metadata:
name: complete-metadata-pod
labels:
app: myapp
version: v1.0
tier: backend
environment: production
annotations:
description: "Production application"
owner: "platform-team"
contact: "team@example.com"
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "pod-name"
fieldRef:
fieldPath: metadata.name
- path: "pod-namespace"
fieldRef:
fieldPath: metadata.namespace
- path: "pod-ip"
fieldRef:
fieldPath: status.podIP
- path: "node-name"
fieldRef:
fieldPath: spec.nodeName
- path: "service-account"
fieldRef:
fieldPath: spec.serviceAccountName
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotationsExample: Container Resources
apiVersion: v1
kind: Pod
metadata:
name: resources-volume-pod
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "memory-request"
resourceFieldRef:
containerName: app
resource: requests.memory
- path: "memory-limit"
resourceFieldRef:
containerName: app
resource: limits.memory
- path: "cpu-request"
resourceFieldRef:
containerName: app
resource: requests.cpu
- path: "cpu-limit"
resourceFieldRef:
containerName: app
resource: limits.cpuapiVersion: apps/v1
kind: Deployment
metadata:
name: self-aware-app
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
version: v1.0
tier: backend
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
env:
# Pod identification
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# Labels for application logic
- name: APP_VERSION
valueFrom:
fieldRef:
fieldPath: metadata.labels['version']
- name: APP_TIER
valueFrom:
fieldRef:
fieldPath: metadata.labels['tier']
# Resource limits for tuning
- name: MEMORY_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- name: CPU_LIMIT
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"apiVersion: v1
kind: Pod
metadata:
name: logging-pod
labels:
app: logger
environment: production
spec:
containers:
- name: app
image: myapp:latest
env:
# Pod context for structured logging
- name: LOG_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: LOG_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LOG_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: LOG_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: LOG_ENVIRONMENT
valueFrom:
fieldRef:
fieldPath: metadata.labels['environment']
# Application uses these for structured logs
# Example log: {"level":"info","pod":"logging-pod","namespace":"default","node":"node-1","message":"Started"}apiVersion: v1
kind: Pod
metadata:
name: resource-aware-pod
spec:
containers:
- name: app
image: java-app:latest
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
env:
# JVM can use these to set heap size
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
- name: CPU_LIMIT_CORES
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"
# Application calculates optimal settings
# Example: JVM heap = 80% of MEMORY_LIMIT_MB
# Example: Thread pool size = CPU_LIMIT_CORES * 2apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
labels:
app: myapp
version: v1.0
spec:
containers:
# Main application
- name: app
image: myapp:latest
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
# Sidecar for monitoring
- name: monitor
image: monitor:latest
env:
- name: MONITORED_POD
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MONITORED_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: true
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotationsapiVersion: v1
kind: Pod
metadata:
name: dynamic-config-pod
labels:
app: myapp
feature-flag-new-ui: "true"
feature-flag-beta-api: "false"
cache-enabled: "true"
spec:
containers:
- name: app
image: myapp:latest
env:
# Feature flags from labels
- name: FEATURE_NEW_UI
valueFrom:
fieldRef:
fieldPath: metadata.labels['feature-flag-new-ui']
- name: FEATURE_BETA_API
valueFrom:
fieldRef:
fieldPath: metadata.labels['feature-flag-beta-api']
- name: CACHE_ENABLED
valueFrom:
fieldRef:
fieldPath: metadata.labels['cache-enabled']Choose the right method for your use case.
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.namevolumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labelsProblem: Trying to access fields not supported by Downward API.
Solution: Use only supported fields:
# Bad: Not supported
fieldPath: spec.containers[0].image
# Good: Supported fields
fieldPath: metadata.name
fieldPath: status.podIPProblem: Referencing wrong container in resourceFieldRef.
Solution: Use correct container name:
# Bad: Wrong container name
resourceFieldRef:
containerName: wrong-name
resource: limits.memory
# Good: Correct container name
resourceFieldRef:
containerName: app
resource: limits.memoryProblem: Referencing non-existent label/annotation.
Solution: Ensure label/annotation exists:
metadata:
labels:
app: myapp # Must exist
env:
- name: APP_LABEL
valueFrom:
fieldRef:
fieldPath: metadata.labels['app']Problem: Expecting environment variables to update.
Solution: Environment variables are set at startup only. Use volumes for updates:
# Environment variables don't update
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# Volumes may update for some fields
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labelsProblem: Using wrong divisor for resource conversion.
Solution: Use appropriate divisor:
# For memory in MB
divisor: 1Mi
# For memory in GB
divisor: 1Gi
# For CPU in cores
divisor: "1"
# For CPU in millicores (default)
# No divisor neededChoose clear names that indicate source:
# Good: Clear naming
- name: POD_NAME
- name: POD_NAMESPACE
- name: NODE_NAME
# Avoid: Unclear naming
- name: NAME
- name: NS
- name: NODEOrganize environment variables logically:
env:
# Pod identification
- name: POD_NAME
- name: POD_NAMESPACE
- name: POD_IP
# Resource limits
- name: MEMORY_LIMIT
- name: CPU_LIMIT
# Labels
- name: APP_VERSION
- name: ENVIRONMENTWhen you need all labels or annotations:
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotationsConvert resources to useful units:
# Memory in megabytes for application tuning
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1Mi
# CPU in cores for thread pool sizing
- name: CPU_LIMIT_CORES
valueFrom:
resourceFieldRef:
resource: limits.cpu
divisor: "1"Add comments explaining how metadata is used:
env:
# Used for distributed tracing
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# Used for JVM heap sizing (80% of limit)
- name: MEMORY_LIMIT_MB
valueFrom:
resourceFieldRef:
resource: limits.memory
divisor: 1MiCheck metadata exposed to containers.
sudo kubectl exec <pod-name> -- env | grep PODsudo kubectl exec <pod-name> -- ls -la /etc/podinfo
sudo kubectl exec <pod-name> -- cat /etc/podinfo/labelssudo kubectl describe pod <pod-name>Verify field path is correct:
sudo kubectl get pod <pod-name> -o yamlsudo kubectl get pod <pod-name> --show-labels
sudo kubectl describe pod <pod-name>Ensure container name matches:
sudo kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].name}'In episode 24, we've explored Downward API in Kubernetes. We've learned how to expose Pod and container metadata to applications, use fieldRef and resourceFieldRef, and practical use cases for self-aware applications.
Key takeaways:
metadata.labels['key']metadata.labels (volume only)Downward API is essential for building self-aware applications in Kubernetes. By understanding how to expose and use Pod metadata, you can create applications that adapt to their runtime environment and provide better observability.
Are you getting a clearer understanding of Downward API in Kubernetes? Keep your learning momentum going and look forward to the next episode!