Projected Volumes in Kubernetes
Kubernetes introduces a category of volumes that don't serve traditional data storage purposes. Instead, these Projected Volumes inject predefined data directly into containers at runtime. The Kubernetes documentation defines four types of Projected Volumes: Secret, ConfigMap, Downward API, and ServiceAccountToken.
Secret Volume
The Secret volume type enables sensitive credential data to be stored in etcd and mounted into containers as files. This approach proves particularly useful for database access credentials and API keys.
Consider a scenario where database credentials need to be injected into a container:
credentials-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: db-client-pod
spec:
containers:
- name: db-client
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: db-credentials
mountPath: "/secrets"
readOnly: true
volumes:
- name: db-credentials
projected:
sources:
- secret:
name: db-username
- secret:
name: db-password
The projected volume references two Secret objects containing the database username and password. To create these secrets from files:
echo -n 'dbadmin' > ./dbuser.txt
echo -n 's3cr3tP@ss' > ./dbpass.txt
kubectl create secret generic db-username --from-file=./dbuser.txt
kubectl create secret generic db-password --from-file=./dbpass.txt
Alternatively, define secrets directly in YAML format using Base64 encoding:
db-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-secrets
type: Opaque
data:
username: ZGJhZG1pbg==
password: czNjcnM0VEBzc3M=
Encoding values to Base64 requires the following transformation:
echo -n 'dbadmin' | base64
# Output: ZGJhZG1pbg==
echo -n 's3cr3tP@ss' | base64
# Output: czNjcnM0VEBzc3M=
Important: Secret data encoded in Base64 is not encrypted by default. Production environments should enable Kubernetes Secret encryption providers for adequate security.
When the Pod executes, the secret files appear in the container's mounted directory:
kubectl apply -f db-secrets.yaml
kubectl apply -f credentials-pod.yaml
kubectl exec -it db-client-pod -- /bin/sh
# Within the container:
ls /secrets/
# Output: db-password db-username
cat /secrets/username
# Output: dbadmin
Kubernetes automatically syncs volume contents when etcd data changes. Applications should implement retry logic and connection timeouts to handle potential synchronization delays.
ConfigMap Volume
ConfigMap volumes serve the same purpose as Secrets but target non-sensitive configuration data. A Java application might store its properties file in a ConfigMap:
# application.properties content
server.port=8080
app.name=my-application
log.level=INFO
kubectl create configmap app-config --from-file=./application.properties
kubectl get configmaps app-config -o yaml
The resulting ConfigMap preserves the original file structure and content.
Downward API Volume
The Downward API exposes Pod metadata directly into containers through the filesystem. This mechanism allows applications to access Pod-level information without coupling to the Kubernetes API.
metadata-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: metadata-consumer
labels:
environment: production
tier: backend
spec:
containers:
- name: app-container
image: busybox
command: ["sh", "-c"]
args:
- while true; do
echo "=== Pod Labels ===";
cat /etc/podmeta/labels;
sleep 10;
done;
volumeMounts:
- name: pod-metadata
mountPath: /etc/podmeta
volumes:
- name: pod-metadata
projected:
sources:
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
The Downward API supports numerous fields through fieldRef:
fieldRef available fields:
spec.nodeName- Host node namestatus.hostIP- Host IP addressmetadata.name- Pod namemetadata.namespace- Pod namespacestatus.podIP- Pod IP addressmetadata.uid- Pod UIDmetadata.labels['<KEY>']- Specific label valuemetadata.annotations['<KEY>']- Specific annotation valuemetadata.labels- All labelsmetadata.annotations- All annotations
resourceFieldRef available fields:
- Container CPU and memory limits
- Container CPU and memory requests
Downward API can only expose information available before container startup. Runtime data like process IDs require sidecar containers instead.
ServiceAccountToken Volume
ServiceAccount objects provide identity for workloads running within the cluster. Each ServiceAccount links to a dedicated Secret containing authentication tokens and certificates.
Kubernetes automatically provisions a default ServiceAccount for every namespace. All Pods automatically receive this ServiceAccount's token via automatic volume mounting:
kubectl describe pod nginx-789f7d5c5-abc12
# Relevant output:
Volumes:
default-token-xyz89:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-xyz89
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-xyz89 (ro)
The mounted directory contains essential authentication files:
ls /var/run/secrets/kubernetes.io/serviceaccount/
# Output: ca.crt namespace token
Applications using the official Kubernetes client libraries can leverage this automatic configuration through "InClusterConfig"—the standard approach for cluster-internal API access.
To prevent automatic token mounting, disable it at the Pod level:
spec:
automountServiceAccountToken: false
Container Health Probes
Kubernetes delegates container health determination to kubelet through configurable probes rather than relying solely on container runtime status.
Liveness Probe
The liveness probe determines whether a container requires restarting. This example demonstrates file-based health checking:
liveness-demo.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
check: liveness
name: process-monitor
spec:
containers:
- name: monitored-process
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/running; sleep 30; rm -tmp/running; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/running
initialDelaySeconds: 5
periodSeconds: 5
This container creates /tmp/running at startup, simulating a health indicator. After 30 seconds, the file disappears, triggering the liveness probe to fail.
kubectl create -f liveness-demo.yaml
# Initial status - healthy
kubectl get pod process-monitor
NAME READY STATUS RESTARTS AGE
process-monitor 1/1 Running 0 10s
# After 30 seconds - probe fails, container restarts
kubectl describe pod process-monitor
# Events show: Liveness probe failed
kubectl get pod process-monitor
NAME READY STATUS RESTARTS AGE
process-monitor 1/1 Running 1 1m
The container restarted (RESTARTS incremented from 0 to 1), but the Pod maintains Running status because kubelet recreated the container locally rather than migrating it.
Alternative Probe Configurations
HTTP Health Check:
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Health-Check
value: enabled
initialDelaySeconds: 3
periodSeconds: 3
TCP Socket Check:
livenessProbe:
tcpSocket:
port: 5432
initialDelaySeconds: 15
periodSeconds: 20
ReadinesProbe
Unlike liveness probes, readinessProbe affects Service routing rather than container lifecycle. A container failing its readiness probe gets excluded from Service load balancing until it recovers.
Pod Recovery Mechanism
The restartPolicy field controls container recovery behavior after failures:
spec:
restartPolicy: Always | OnFailure | Never
Policy behaviors:
- Always: Container restarts regardless of exit code. Suitable for long-running services like web servers.
- OnFailure: Container restarts only on non-zero exit codes. Appropriate for batch jobs that complete successfully.
- Never: Container does not restart. Use when preserving exit state and logs matters.
Critical constraint: Pod recovery always occurs on the original node. Pods never migrate automatically to different nodes unless explicitly rescheduled through controllers like Deployments.
Multi-container Pod behavior:
When a Pod contains multiple containers and one fails:
- With
restartPolicy=Always: Only the failed container restarts, Pod stays Running - With
restartPolicy=Never: Pod stays Running, failed container stays terminated - All containers must fail before the entire Pod enters Failed state
kubectl get pod multi-container-pod
NAME READY STATUS RESTARTS AGE
multi-container-pod 0/2 Running 1 5m
READY 0/2 indicates one of two containers is not ready.
PodPreset for Automated Field Injection
PodPreset objects automatically augment Pod specifications at creation time, enabling separation between developer-provided configurations and operations-managed requirements.
Basic Pod submitted by developer:
frontend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web-frontend
labels:
component: frontend
spec:
containers:
- name: web-server
image: nginx
ports:
- containerPort: 80
Operations-defined PodPreset:
cache-preset.yaml
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: cache-configuration
spec:
selector:
matchLabels:
component: frontend
env:
- name: REDIS_PORT
value: "6380"
volumeMounts:
- mountPath: /tmp/cache
name: shared-cache
volumes:
- name: shared-cache
emptyDir: {}
When the PodPreset selector matches the Pod's labels, Kubernetes merges the configurations:
kubectl create -f cache-preset.yaml
kubectl create -f frontend-pod.yaml
kubectl get pod web-frontend -o yaml
The resulting Pod specification includes injected values:
apiVersion: v1
kind: Pod
metadata:
name: web-frontend
labels:
component: frontend
annotations:
podpreset.admission.kubernetes.io/cache-configuration: "resource version"
spec:
containers:
- name: web-server
image: nginx
ports:
- containerPort: 80
env:
- name: REDIS_PORT
value: "6380"
volumeMounts:
- mountPath: /tmp/cache
name: shared-cache
volumes:
- name: shared-cache
emptyDir: {}
The annotation indicates PodPreset modification. When multiple PodPresets target the same Pod, Kubernetes merges their changes. Conflicting field values remain unmodified from the original specification.