Environment variables
An environment variable is a name and a value that your app reads when it starts, like PORT=4000. In Kubernetes you write them in the YAML, under the container. That is really all there is to it. This chapter shows how, slowly.
What it is
Think of it as a sticky note attached to your app when it starts:
You already used them with Docker:
$ docker run -e PORT=4000 -e LOG_LEVEL=debug micro-posts
Kubernetes does exactly the same thing. You just write it in the YAML instead of the command.
Why use them
So one image can run in different places with different settings, without rebuilding it:
Typical things to put in env vars: ports, URLs of other services, log levels, feature switches.
How to write them
Add an env: list inside the container. Each item has a name and a value. Click the highlighted parts:
apiVersion: apps/v1
kind: Deployment
metadata:
name: posts-depl
spec:
replicas: 1
selector:
matchLabels:
app: posts
template:
metadata:
labels:
app: posts
spec:
containers:
- name: posts
image: micro-posts:1.0
env:
- name: EVENT_BUS_URL
value: http://event-bus-srv:4005
- name: LOG_LEVEL
value: debug
- name: PORT
value: "4000"
env:
Goes inside the container, at the same level as name and image. Each container in a Pod has its own env.
- name:
The variable name. Each - starts a new variable. By convention: CAPITAL_LETTERS_WITH_UNDERSCORES.
value:
The text your app will get. Indented under its name, lined up with it.
"4000" in quotes
Numbers and true/false need quotes. Env values must be text, and without quotes YAML would read 4000 as a number and Kubernetes would reject the file.
If you remember the Pod chapter: it is the same containers list, with one more key. In a bare Pod it goes in exactly the same place.
Try it
Change names and values, add or remove variables. Try a value of true or 3000 and watch the quotes appear.
Reading them in Node
// posts/index.js
const PORT = Number(process.env.PORT) || 4000;
const EVENT_BUS_URL = process.env.EVENT_BUS_URL || 'http://localhost:4005';
const DEBUG = process.env.DEBUG === 'true';
app.listen(PORT, () => console.log(`Posts listening on ${PORT}`));
- Every value arrives as text.
"4000", not4000;"true", nottrue. Convert it yourself. - Give a default with
||, so the app still runs on your laptop without Kubernetes. - A variable you did not set is
undefined.
Check they arrived
$ kubectl exec deployment/posts-depl -- env
PATH=/usr/local/sbin:/usr/local/bin:...
HOSTNAME=posts-depl-7d9f8c6b5d-4kq2p
EVENT_BUS_URL=http://event-bus-srv:4005
LOG_LEVEL=debug
PORT=4000
...Runs env inside the container and prints every variable it really has. The surest check.
$ kubectl describe pod posts-depl-7d9f8c6b5d-4kq2p
...
Environment:
EVENT_BUS_URL: http://event-bus-srv:4005
LOG_LEVEL: debug
PORT: 4000
...Shows what the Pod was given, without entering it.
Changing a value
Your app reads env vars once, when it starts. A running container never sees a new value. So Kubernetes starts new Pods for you:
env: - name: LOG_LEVEL value: error # was: debugEdit the value in your YAML file.
$ kubectl apply -f posts-depl.yaml deployment.apps/posts-depl configuredApply it, the same as always.
$ kubectl get pods NAME READY STATUS AGE posts-depl-5b7c9d8f4-x2kqp 1/1 Running 4s ← new, LOG_LEVEL=error posts-depl-7d9f8c6b5d-4kq2p 1/1 Terminating 2h ← old, LOG_LEVEL=debugThe env is part of the Pod template, so the Deployment does a rolling update (chapter 10): new Pods with the new value replace the old ones.
$ kubectl set env deployment/posts-depl LOG_LEVEL=error deployment.apps/posts-depl env updatedA shortcut without editing the file. Handy for a quick test, but your YAML file no longer matches what is running, so prefer editing the file.
Three common mistakes
✗ Wrong
- name: PORT
value: 4000
- name: DEBUG
value: true✓ Right
- name: PORT
value: "4000"
- name: DEBUG
value: "true"Error: ... cannot unmarshal number into Go struct field EnvVar.value of type string
The scary message just means: "I expected text, you gave me a number." Add quotes.
✗ Wrong
containers:
- name: posts
image: micro-posts:1.0
env: # outside the container!
- name: PORT
value: "4000"✓ Right
containers:
- name: posts
image: micro-posts:1.0
env: # lined up with image
- name: PORT
value: "4000"env must line up with image. Otherwise it is not part of the container and Kubernetes complains about an unknown field, or the variable is silently missing.
✗ Avoid
- name: DB_PASSWORD
value: "hunter2"Anyone who can read the YAML file, or your Git history, sees the password.
✓ Later
Passwords and API keys go in a Secret, and shared settings in a ConfigMap. Both still end up as ordinary env vars in your app, so everything in this chapter still applies. They get their own chapters (18 and 19).
Recap
- An env var is a name and a value your app reads at start.
- Write them under
env:inside the container:- name: X,value: "y". - Quote numbers and true/false. Your app always receives text.
- Read them with
process.env.X, with a default. - Check with
kubectl exec deployment/<name> -- env. - Changing a value in a Deployment and applying it starts new Pods with the new value.
Quick check
value: "5" for MAX_RETRIES. Your code does if (process.env.MAX_RETRIES > 3). Safe?Show answer
It happens to work because JavaScript converts "5" for >, but it is fragile. Convert explicitly: const MAX_RETRIES = Number(process.env.MAX_RETRIES) || 3;
Next: a whole microservices app in one file, which uses env vars to connect every service.
Environment variables in Kubernetes: common questions
How do I set an environment variable in Kubernetes?
Add an env list inside the container in your Pod or Deployment YAML, with a name and a value for each variable, then kubectl apply.
Why does Kubernetes reject value: 4000 in env?
Env values must be strings. Unquoted 4000 or true are a number and a boolean in YAML. Write value: "4000" or value: "true".