Chapter 16 of 47 · Configuration

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:

Kubernetes hands environment variables to your app at startSticky notePORT = 4000LOG_LEVEL = debugat startYour appcontainerreadsprocess.env.PORT→ "4000"

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:

One image, two settingsimage micro-posts:1.0On your laptopLOG_LEVEL=debugDB_HOST=localhost-dbIn productionLOG_LEVEL=errorDB_HOST=prod-db
Same code, same image. Only the sticky note changes.

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.

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}`));

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.

You will also see variables you never wrote, such as HOSTNAME (the Pod name) and some ..._SERVICE_HOST ones Kubernetes adds automatically. Ignore those for now.

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:

  1.           env:
                - name: LOG_LEVEL
                  value: error          # was: debug

    Edit the value in your YAML file.

  2. $ kubectl apply -f posts-depl.yaml
    deployment.apps/posts-depl configured

    Apply it, the same as always.

  3. $ 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=debug

    The 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.

  4. $ kubectl set env deployment/posts-depl LOG_LEVEL=error
    deployment.apps/posts-depl env updated

    A 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.

Editing a bare Pod's env does not work: most of a running Pod cannot be changed. Delete it and create it again. One more reason to use Deployments.

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.

Recap

Quick check

Your YAML has 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".