1. Overview

In this tutorial, we’ll look at the environment variables in the Kubernetes.

2. Setting Environment Variables in Kubernetes

Container images are usually built with their configurations externalized to environment variables. Specifically, the programs would read the predefined environment variables during runtime to obtain any configuration specified by the users. For example, the official PostgreSQL container image accepts environment variables such as PGDATA for configuring the path to store the data. This makes it easy for users to customize the image without having to rebuild the image whenever we want to change the behaviour of the application.

In Kubernetes, we can set the environment variables for our application using the env and envFrom fields within the ContainerSpec. Let’s look at the fields in detail.

2.1. The env Field

The env field in the ContainerSpec takes as input a list of name-value pair of environment variables. The name of the environment variable will be the key used by the application to retrieve the value. For example, we can create a PostgreSQL pod and set its POSTGRES_PASSWORD environment variable using the env field:

$ cat pg.yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
  - name: postgres
    image: postgres
    env:
    - name: POSTGRES_PASSWORD
      value: mysecretpassword

In the example above, we are defining an environment variable POSTGRES_PASSWORD and setting its value as mysecretpassword. Within the application, we can obtain the value of mysecretpassword by reading the value of the POSTGRES_PASSWORD environment variable.

In addition to the plain text value, we can refer to other resources, such as ConfigMap and Secret, for the value of the environment variables using the valueFrom field. For example, consider that we are storing the POSTGRES_PASSWORD value in a secret with the key PG_PASS:

$ cat pg-pass-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
type: Opaque
data:
  PG_PASS: bXlzZWNyZXRwYXNzd29yZA==

We can refer to this value in our pod manifest using the valueFrom inner field on the env field:

$ cat pg-valuefrom.yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
  - name: postgres
    image: postgres
    env:
    - name: POSTGRES_PASSWORD
      valueFrom:
        secretKeyRef:
          name: postgres-secret
          key: PG_PASS

In addition to the secretKeyRef, the valueFrom field also supports reading environment variable values from ConfigMap using the configMapRef field. Furthermore, we can also refer to an existing pod field and the resource value of the pod using the fieldRef and resourceFieldRef, respectively.

The env field is convenient for setting a few environment variables for the application. However, when the number of environment variables gets large, it’s more convenient to group them in a single ConfigMap and use the envFrom syntax instead.

2.2. The envFrom Field

The envFrom field of the ContainerSpec is similar to the env field in that it allows us to export the environment variable to the application. However, the envFrom field differs from the env field in that it accepts a reference to a ConfigMap or Secret resource and exports its key-value pairs as an environment variable in its entirety.

For example, with the same postgres-secret Secret resource we have in the cluster, we can directly export the PG_PASS as an environment variable in our application using envFrom followed by the name of the secret resource in the secretRef field:

$ cat pg-envfrom.yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
  - name: postgres
    image: postgres
    envFrom:
    - secretRef:
        name: postgres-secret

Contrary to the env field, we don’t get to specify the environment variable name in the envFrom field. When we inject environment variables using the envFrom field, the key in the key-value pair of the ConfigMap or Secret will be the environment variable name. Since we use the PG_PASS as our key in the postgres-secret, that’s the name of the environment variable we have to refer to in our application to obtain the value.

3. Overriding Environment Variables

There are three different ways the application can receive the environment variables value: the env field, the envFrom fields, and the default environment variables in the container image. In the event that the environment variables from the three different sources conflict, the Kubernetes controller resolves the value of the environment variable following a specific order of precedence.

Therefore, to override an environment variable, we can redefine the same environment variable with different values on a layer that has higher priority during conflict resolution.

3.1. Order of Precedence

Out of the three sources, the environment variables in the env field take the utmost priority. Additionally, within the env field itself, the key may be repeated. When that happens, the value of the repeating key that appears last in the list will take priority.

Then, the environment variables defined in the envFrom are the next priority in line. Specifically, any environment variables that are imported on envFrom but do not exist on the env field will show up in the application during runtime. Finally, the container image environment variables definition will take effect if neither the env nor envFrom shares the same key.

One common mistake is to specify the environment variables in the env field and then attempt to override it by defining it in a ConfigMap and using the envFrom field. This doesn’t work because the environment variables injected through envFrom has lower priority than the env field.

Let’s look at the method for overriding the environment variable by referring to the value from a ConfigMap resource.

3.2. Overriding Environment Variable With Repeated Keys

We can override an environment variable within the same env field by repeating the same key. Do take note that the effective value we want for the environment variable must appear last in the env list. For example, we can override the POSTGRES_PASSWORD environment variable with a different value:

$ cat pg.yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
  - name: postgres
    image: postgres
    env:
    - name: POSTGRES_PASSWORD
      value: oldpassword
    - name: POSTGRES_PASSWORD
      value: newpassword

With the pod definition above, the effective value of the environment variable POSTGRES_PASSWORD will be newpassword. This is due to the fact that the Kubernetes controller resolves conflicting environment variable keys within the env field by taking the last appearing value as the final value.

This technique also works with the valueFrom syntax that retrieves the value from a ConfigMap or Secret:

$ cat pg-override-valuefrom.yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  containers:
  - name: postgres
    image: postgres
    env:
    - name: POSTGRES_PASSWORD
      value: oldpassword
    - name: POSTGRES_PASSWORD
      valueFrom:
        secretKeyRef:
          name: postgres-secret
          key: PG_PASS

The second definition of the POSTGRES_PASSWORD overrides the value using the PG_PASS value from the postgres-secret resource.

4. Conclusion

In this tutorial, we’ve learned that environment variables are a common way of configuring container images. In Kubernetes, environment variables are set through the pod definition. Then, we’ve looked at three different ways for injecting environment variables into the workload. Finally, we’ve learned about the precedence order of these different ways of injecting the environment variables as well as the method for overriding the environment variables.