Learning Kubernetes - Downward API
Episode 24 of 47

Learning Kubernetes - Downward API

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.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 30, 2026
0 views
9 min read

Introduction

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.

What Is Downward API?

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:

  • Metadata exposure - Access Pod and container information
  • No API calls - No need to query Kubernetes API server
  • Two methods - Environment variables or volume files
  • Dynamic values - Reflects current Pod state
  • Self-awareness - Applications know their context
  • Resource information - Access limits and requests

Why Use Downward API?

Downward API solves several important use cases:

  • Application identification - Know Pod name and namespace
  • Resource awareness - Adapt to memory and CPU limits
  • Label-based logic - Make decisions based on labels
  • Node information - Know which node the Pod runs on
  • Service account - Access service account name
  • Logging context - Include Pod metadata in logs
  • Monitoring tags - Tag metrics with Pod information
  • Dynamic configuration - Configure based on Pod metadata

Without Downward API, applications would need to call the Kubernetes API server or rely on external configuration, adding complexity and dependencies.

Available Metadata Fields

The Downward API exposes various Pod and container metadata.

Pod Metadata Fields

Available via fieldRef:

  • metadata.name - Pod name
  • metadata.namespace - Pod namespace
  • metadata.uid - Pod unique ID
  • metadata.labels['<KEY>'] - Specific label value
  • metadata.labels - All labels (volume only)
  • metadata.annotations['<KEY>'] - Specific annotation value
  • metadata.annotations - All annotations (volume only)

Pod Status Fields

Available via fieldRef:

  • status.podIP - Pod IP address
  • status.hostIP - Node IP address
  • spec.serviceAccountName - Service account name
  • spec.nodeName - Node name where Pod runs

Container Resource Fields

Available via resourceFieldRef:

  • requests.cpu - CPU request
  • requests.memory - Memory request
  • requests.ephemeral-storage - Ephemeral storage request
  • limits.cpu - CPU limit
  • limits.memory - Memory limit
  • limits.ephemeral-storage - Ephemeral storage limit

Using Downward API with Environment Variables

Expose metadata as environment variables in containers.

Pod Metadata as Environment Variables

Example: Basic Pod Information

Kubernetespod-metadata-env.yml
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.nodeName

Labels and Annotations as Environment Variables

Example: Accessing Labels

Kuberneteslabels-env.yml
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

Kubernetesannotations-env.yml
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']

Resource Limits as Environment Variables

Example: Container Resources

Kubernetesresources-env.yml
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-storage

Resource Divisor

Convert resource values to different units.

Example: Memory in Megabytes

Kubernetesresource-divisor.yml
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"

Using Downward API with Volumes

Expose metadata as files in a volume.

Pod Metadata as Files

Example: Basic Volume Mount

Kubernetespod-metadata-volume.yml
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.annotations

Files created:

  • /etc/podinfo/name - Contains Pod name
  • /etc/podinfo/namespace - Contains namespace
  • /etc/podinfo/labels - Contains all labels
  • /etc/podinfo/annotations - Contains all annotations

All Labels and Annotations

Example: Complete Metadata

Kubernetescomplete-metadata-volume.yml
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.annotations

Resource Information as Files

Example: Container Resources

Kubernetesresources-volume.yml
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.cpu

Practical Examples

Example 1: Application with Self-Awareness

Kubernetesself-aware-app.yml
apiVersion: 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"

Example 2: Logging with Context

Kuberneteslogging-context.yml
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"}

Example 3: Resource-Aware Application

Kubernetesresource-aware-app.yml
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 * 2

Example 4: Multi-Container with Shared Metadata

Kubernetesmulti-container-metadata.yml
apiVersion: 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.annotations

Example 5: Dynamic Configuration

Kubernetesdynamic-config.yml
apiVersion: 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']

Environment Variables vs Volumes

Choose the right method for your use case.

Use Environment Variables When:

  • Need specific metadata fields
  • Simple key-value pairs
  • Application reads environment variables
  • Small amount of data
  • Static during container lifetime
Kubernetesyml
env:
    - name: POD_NAME
      valueFrom:
          fieldRef:
              fieldPath: metadata.name

Use Volumes When:

  • Need all labels or annotations
  • Large amount of metadata
  • Application reads files
  • Want updates without restart (for some fields)
  • Multiple containers sharing metadata
Kubernetesyml
volumes:
    - name: podinfo
      downwardAPI:
          items:
              - path: "labels"
                fieldRef:
                    fieldPath: metadata.labels

Common Mistakes and Pitfalls

Mistake 1: Accessing Unavailable Fields

Problem: Trying to access fields not supported by Downward API.

Solution: Use only supported fields:

Kubernetesyml
# Bad: Not supported
fieldPath: spec.containers[0].image
 
# Good: Supported fields
fieldPath: metadata.name
fieldPath: status.podIP

Mistake 2: Wrong Container Name

Problem: Referencing wrong container in resourceFieldRef.

Solution: Use correct container name:

Kubernetesyml
# Bad: Wrong container name
resourceFieldRef:
    containerName: wrong-name
    resource: limits.memory
 
# Good: Correct container name
resourceFieldRef:
    containerName: app
    resource: limits.memory

Mistake 3: Missing Label or Annotation

Problem: Referencing non-existent label/annotation.

Solution: Ensure label/annotation exists:

Kubernetesyml
metadata:
    labels:
        app: myapp  # Must exist
env:
    - name: APP_LABEL
      valueFrom:
          fieldRef:
              fieldPath: metadata.labels['app']

Mistake 4: Expecting Updates

Problem: Expecting environment variables to update.

Solution: Environment variables are set at startup only. Use volumes for updates:

Kubernetesyml
# 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.labels

Mistake 5: Incorrect Divisor

Problem: Using wrong divisor for resource conversion.

Solution: Use appropriate divisor:

Kubernetesyml
# 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 needed

Best Practices

Use Descriptive Environment Variable Names

Choose clear names that indicate source:

Kubernetesyml
# Good: Clear naming
- name: POD_NAME
- name: POD_NAMESPACE
- name: NODE_NAME
 
# Avoid: Unclear naming
- name: NAME
- name: NS
- name: NODE

Organize environment variables logically:

Kubernetesyml
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: ENVIRONMENT

Use Volumes for All Labels/Annotations

When you need all labels or annotations:

Kubernetesyml
volumes:
    - name: podinfo
      downwardAPI:
          items:
              - path: "labels"
                fieldRef:
                    fieldPath: metadata.labels
              - path: "annotations"
                fieldRef:
                    fieldPath: metadata.annotations

Set Appropriate Divisors

Convert resources to useful units:

Kubernetesyml
# 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"

Document Metadata Usage

Add comments explaining how metadata is used:

Kubernetesyml
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: 1Mi

Viewing Downward API Data

Check metadata exposed to containers.

View Environment Variables

Kubernetesbash
sudo kubectl exec <pod-name> -- env | grep POD

View Volume Files

Kubernetesbash
sudo kubectl exec <pod-name> -- ls -la /etc/podinfo
sudo kubectl exec <pod-name> -- cat /etc/podinfo/labels

Describe Pod

Kubernetesbash
sudo kubectl describe pod <pod-name>

Troubleshooting Downward API

Check Field Path

Verify field path is correct:

Kubernetesbash
sudo kubectl get pod <pod-name> -o yaml

Verify Labels and Annotations

Kubernetesbash
sudo kubectl get pod <pod-name> --show-labels
sudo kubectl describe pod <pod-name>

Check Container Name

Ensure container name matches:

Kubernetesbash
sudo kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].name}'

Conclusion

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:

  • Downward API exposes Pod metadata to containers
  • Two methods: environment variables and volume files
  • Use fieldRef for Pod metadata (name, namespace, labels, etc.)
  • Use resourceFieldRef for container resources (limits, requests)
  • Environment variables set at container startup
  • Volume files may update for some fields
  • Access specific labels with metadata.labels['key']
  • Access all labels with metadata.labels (volume only)
  • Use divisor to convert resource units
  • No API calls needed - metadata injected automatically
  • Enables self-aware applications that adapt to context
  • Useful for logging, monitoring, and resource tuning
  • Choose environment variables for simple values
  • Choose volumes for all labels/annotations

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!