Environment Variables in Kubernetes
Environment Variables in Kubernetes help containerized apps configure. Key-value pairs injected into a container’s runtime environment let developers change application behavior without rewriting source code or container image. Modern cloud-native principles include isolating application settings from the container image and using a single protected image across development, testing, and production.
You can also read What is a Kubernetes ReplicaSet & Working with ReplicaSets
Methods of Injecting Environment Variables
Direct Definition (Hard-coding)
The simplest method to set an environment variable is to define it directly within the spec.containers.env block of a Pod or Deployment manifest. Despite being simple to use, this approach is thought to be the least flexible since it exposes configuration data in version control and necessitates manual updates to the manifest for each modification.
Example of Hard-coded Environment Variables:
apiVersion: v1
kind: Pod
metadata:
name: direct-env-demo
spec:
containers:
- name: demo-container
image: nginx
env:
- name: APP_COLOR
value: "blue"
- name: LOG_LEVEL
value: "debug"
In this scenario, the application inside the container can access APP_COLOR and LOG_LEVEL as standard environment variables. You can verify this by running kubectl exec direct-env-demo -- printenv, which will list all variables active in the container.
Using ConfigMaps for Externalised Configuration
Hostnames, service ports, and account names are examples of non-sensitive configuration parameters that are stored in ConfigMaps, special Kubernetes objects. Data stored in a ConfigMap allows you to modify settings without regard to the Pod lifecycle. ConfigMaps can be used in two main ways for environment variables:
- configMapKeyRef: Inserts a particular ConfigMap key-value pair.
- envFrom: Injects each pair of keys and values from a ConfigMap as a separate environment variable, maybe with a prefix.
Example using ConfigMap References:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database_url: "mysql-service"
db_port: "3306"
---
apiVersion: v1
kind: Pod
metadata:
name: configmap-env-demo
spec:
containers:
- name: demo-container
image: nginx
env:
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: database_url
envFrom:
- configMapRef:
name: app-config
Using envFrom is particularly useful when managing a large number of variables, as it reduces the verbosity of the Pod manifest.
You can also read What is Kubernetes Cloud Controller Manager?
Using Secrets for Sensitive Data
Kubernetes’ Secret resource stores passwords, API keys, and certificates. Despite resembling ConfigMaps, Secrets hide information with base64. To avoid plaintext exposure in manifests, it is recommended that any confidential data be stored in Secrets rather than ConfigMaps or hard-coded values.
Example using Secret References:
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # "password123" in base64
---
apiVersion: v1
kind: Pod
metadata:
name: secret-env-demo
spec:
containers:
- name: demo-container
image: nginx
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
Note that environment variables set via env or envFrom will override any environment variables specified in the container image.
Defining Dependent Environment Variables
You can define environment variables in Kubernetes that make reference to other variables declared inside the same container. This is accomplished via the $(VAR_NAME) syntax.
Important Rules for Dependencies:
- Order Matters: Only environment variables earlier in the
envlist are “defined.” - Undefined Variables: Undefined variables are treated as literal strings (e.g.,
$(UNDEFINED)remains$(UNDEFINED)). - Escaping: Escape syntax with a double dollar sign (
$$(VAR_NAME)) to prevent expansion.
Example of Dependent Variables:
apiVersion: v1
kind: Pod
metadata:
name: dependent-env-demo
spec:
containers:
- name: demo-container
image: nginx
env:
- name: PROTOCOL
value: "https"
- name: HOSTNAME
value: "example.com"
- name: SERVICE_ADDRESS
value: "$(PROTOCOL)://$(HOSTNAME)"
In this example, SERVICE_ADDRESS will resolve to https://example.com.
You can also read What is a Kubernetes Controller Manager?
The Downward API and Resource References
With the help of the Downward API, containers can obtain data about the cluster or themselves without the user having to explicitly provide it. This covers container resource needs as well as pod metadata.
- fieldRef: Used to inject metadata at the Pod level, such as the IP address, name, namespace, or node on which the Pod is operating.
- resourceFieldRef: Used to inject resource restrictions and requests (CPU and RAM) at the container level.
Example using fieldRef and resourceFieldRef:
apiVersion: v1
kind: Pod
metadata:
name: downward-api-demo
spec:
containers:
- name: demo-container
image: nginx
resources:
limits:
memory: "128Mi"
env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: MEM_LIMIT
valueFrom:
resourceFieldRef:
containerName: demo-container
resource: limits.memory
Kubernetes resolves resource values into numeric bytes (e.g., 134217728 for 128Mi) before injecting them.
Loading Environment Variables from Files (v1.34+ Beta)
A new feature in Kubernetes v1.34 enables environment variables to be loaded from files created during runtime, usually by an initContainer. This is controlled by the fileKeyRef field.
How it works:
- Environment variables are written by an
initContainerto a file located in anemptyDirdisk. fileKeyRefis used by the main container to refer to this file.- These values are retrieved and exposed as environment variables by the Kubelet during container startup.
Env File Syntax Rules:
VAR='value'is the required format for variables.- Single quotes must surround values.
- Spaces surrounding the
=symbol are disregarded. - It is not permitted to use interpolation or double quotations.
You can also read What is Kube-Proxy in Kubernetes and it’s Lifecycle
Automatic Service Discovery Variables
Kubernetes injects environment variables into each Pod for all active services in a namespace. These variables are formatted as {SERVICE_NAME}_SERVICE_HOST and {SERVICE_NAME}_SERVICE_PORT. With a backend-api service, pods will automatically get:
BACKEND_API_SERVICE_HOST: The stable internal IP (ClusterIP).BACKEND_API_SERVICE_PORT: The network port.
Note: In order for these variables to be injected, the Service must be built before the Pod; as a result, many contemporary systems choose employing Cluster DNS for service discovery.
Referencing Variables in Startup Commands
Environment variables allow container launch command modification. If environment variables are defined, they may be utilized in the command or args portion as $(VAR_NAME).
Example of Variables in Startup Commands:
spec:
containers:
- name: utility
image: busybox
env:
- name: GREETING
value: "Hello"
- name: USER_NAME
value: "Nigel"
command: ["/bin/sh", "-c", "echo $(GREETING) $(USER_NAME)"]
When this container runs, it will print “Hello Nigel” to the logs.
Management, Lifecycle, and Best Practices
In Kubernetes, environment variables are unchangeable and static once a pod is operating. The operating container won’t reflect any changes made to a ConfigMap or Secret value. A rollout on a higher-level controller, such as a Deployment, is usually used to restart the Pod in order to apply updates.
Summary of Best Practices:
- Security: Instead of using environment variables, mount Secrets as volumes for extremely sensitive data. Environment variables are sometimes available to anyone who may
execinto the container, or they may be disclosed in logs. - Decoupling: To keep your container images generic and portable, always choose ConfigMaps and Secrets over hard-coding values.
- Validation: If the
optionalfield is set tofalsewhile usingfileKeyRef, make sure the keys are present in the file because the container won’t start properly if the keys are absent.
You can also read Kind: A Practical Guide to Local Kubernetes Clusters
