1. Overview

Decoupling application code from the data it uses is a best practice in software development. It enables the application to move from one environment to another without the need to modify it. This is because environment-specific configuration is stored outside the code.

One very popular example of this is using environment variables. Environment variables store data that the application can use without hardcoding them inside the application logic.

In this tutorial, we’ll explain how we can pass environment variables in Docker using env-file and some of its equivalents in Kubernetes, which are ConfigMaps and Secrets.

2. Using –env-file in Docker

A very common method of injecting environment variables inside Docker containers is by using an env-file. This is a text file that contains key-value pairs that represent our environment variables. We create the file using any text editor, add our environment variable key-value pairs, and then pass this file to our container at runtime by using the –env-file command option.

Let’s check this with an example:

$ echo DB_HOST=test > env-vars.txt
$ echo APP=backend >> env-vars.txt
$ cat env-vars.txt
DB_HOST=test
APP=backend

Here we created an env-file with the name env-vars.txt and added two environment variables, DB_HOST and APP.

Now let’s start our container and pass this file to it:

$ docker run -it --env-file env-vars.txt alpine:latest /bin/sh
/ # env
HOSTNAME=984d3294e8a3
SHLVL=1
HOME=/root
APP=backend
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
DB_HOST=test

Here we connected to our container using an interactive shell and used the env command to print our environment variables. We can see the two environment variables that we passed from the file showing in the output.

So now, an application running inside this container can access and use these environment variables.

3. Kubernetes ConfigMap

When managing our containers with Kubernetes, we use what is called a ConfigMap to pass environment variables. ConfigMaps are Kubernetes API objects that we use to store configuration data. Then we can use this data inside the containers by referencing the ConfigMap inside our Pod spec.

Unlike an env-file, ConfigMaps are not just plain files with key-value pairs. Since ConfigMaps are Kubernetes objects, we create them by using a YAML manifest. Then we add our key-value pairs to a data section inside this manifest.

Let’s check our ConfigMap manifest file:

$ cat env-vars.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  DB_HOST: test  
  APP: backend

Here we’ve our manifest file env-vars.yaml with the ConfigMap name myconfigmap and our environment variables DB_HOST and APP.

Let’s deploy this ConfigMap to the cluster using the kubectl command:

$ kubectl apply -f env-vars.yaml 
configmap/myconfigmap created

Now our ConfigMap myconfigmap is created in the Kubernetes cluster, so next, we need to create our Pod and reference this ConfigMap to start using it.

Let’s check our Pod manifest file:

$ cat mypod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mycontainer
      image: alpine
      command: ["sleep", "3600"]
      envFrom:
      - configMapRef:
          name: myconfigmap

Here we’ve our Pod manifest file mypod.yaml, and we’ve referenced our ConfigMap under envFrom to use its data as environment variables.

Let’s deploy our Pod to the cluster:

$ kubectl apply -f mypod.yaml 
pod/mypod created

Now let’s connect to the container’s shell and print our environment variables:

$ kubectl exec -it mypod -- /bin/sh
/ # env
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=mypod
SHLVL=1
HOME=/root
APP=backend
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
DB_HOST=test

Here we can see our two environment variables in the output, so this means that our container has successfully read its data from the ConfigMap.

4. Kubernetes Secrets

Secrets are Kubernetes objects that we use specifically to store sensitive data like passwords, keys, or API tokens. They’re similar to ConfigMaps, but they store information in base64 encoding instead of plain text.

The steps for creating and using Secrets are almost the same as ConfigMaps. We create our Secret manifest with our encoded data, then we create our Pod and reference the Secret for our environment variables.

So let’s first encode our data using the base64 command:

$ echo PASSWORD123 | base64
UEFTU1dPUkQxMjMK
$ echo MySecretKey | base64
TXlTZWNyZXRLZXkK

Here our sensitive information is PASSWORD123 and MySecretKey. We pass this data to the base64 command, which returns the encoded value.

Now let’s create our Secret manifest:

$ cat mysecret.yaml 
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  db_password: UEFTU1dPUkQxMjMK
  secret_key: TXlTZWNyZXRLZXkK

Here we added our data in the encoded format instead of plain text.

Let’s deploy our Secret to the cluster:

$ kubectl apply -f mysecret.yaml 
secret/mysecret created

Now that our Secret is created on the cluster, let’s create our Pod manifest and reference this Secret:

$ cat mysecretpod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: mysecretpod
spec:
  containers:
    - name: mysecretcontainer
      image: alpine
      command: ["sleep", "3600"]
      envFrom:
      - secretRef:
          name: mysecret

Let’s deploy this Pod to the cluster:

$ kubectl apply -f mysecretpod.yaml 
pod/mysecretpod created

Now we’ll open a shell to our container and print the environment variables:

$ kubectl exec -it mysecretpod -- /bin/sh
/ # env
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=mysecretpod
SHLVL=1
HOME=/root
db_password=PASSWORD123
TERM=xterm
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
secret_key=MySecretKey
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/

We can see our two environment variables here decoded with their plain values inside the container. So now, an application running inside this container can use these values seamlessly.

5. Conclusion

In this article, we’ve covered the use of Docker env-file and its equivalent ConfigMaps and Secrets in Kubernetes. Docker env-file provides a way to pass environment variables to containers through an external text file instead of hardcoding them inside the container image. This enables decoupling environment-specific configuration from the container lifecycle.

ConfigMaps are Kubernetes objects that we can use for passing environment variables to containers running on Kubernetes. We add environment variables inside ConfigMaps as key-value pairs and then reference them in our Pod. Secrets provide similar functionality to ConfigMaps, but they’re used to store sensitive data that need to be secured, like passwords, keys, and tokens.