1. Overview

Secrets and ConfigMaps are Kubernetes resources designed to separate the configuration and sensitive information from the application code in a centralized manner. However, each of them has specific use cases and exhibits distinct behavior.

In this tutorial, we’ll explore some prominent differences between ConfigMaps and Secrets in Kubernetes.

2. Creation and Data Encoding

In this section, we will explore some important distinctions between ConfigMaps and Secrets when it comes to their creation.

First, let’s take a look at the my-config.yaml manifest file that shows us different types of fields that we can hold within a ConfigMap:

$ cat my-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  string-value: "Hello, world!"
  number-value: "42"
  boolean-value: "true"
  multiline-value: |
    This is a multiline value
    that spans multiple lines
  list-value: |
    - item1
    - item2
    - item3
  object-value: |
    key1: value1
    key2: value2
  json-value: |-
    {
      "key": "value",
      "array": [1, 2, 3],
      "nested": {
        "innerKey": "innerValue"
      }
    }
  yaml-value: |-
    key: value
    array:
      - 1
      - 2
      - 3
    nested:
      innerKey: innerValue

We must note that ConfigMap supports key-value pairs in plaintext format. Further, we need to specify all these values under the data field.

Now, let’s apply the configuration to create the my-config ConfigMap:

$ kubectl apply -f my-config.yaml
configmap/my-config created

Further, let’s see how we can access one of the values from the newly created my-configmap:

$ kubectl get configmap my-configmap -o jsonpath='{.data.string-value}'
Hello, world!

Next, let’s look into the my-secret.yaml manifest file for creating the my-secret Secret:

$ cat my-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  string-value: SGVsbG8sIHdvcmxkIQ==
  number-value: NDI=
  boolean-value: dHJ1ZQ==
  multiline-value: |
    VGhpcyBpcyBhIG1pbmxpbmUgdmFsdWUgZXZlcnkgdGhhdCBzcGFuIG11bHRpcGxlIGxpbmVzCg==
  json-value: eyAiY29tcGxleCI6ICIxMjM0NSIsICJzdHJpbmciOiAiSGVsbG8sIHdvcmxkISIsICJuZXN0ZWQiOiB7ICJpbnN0YW5jZSI6IDEsICJzdHJpbmciOiAxLCAibmVzdGVkIjogeyAiaW5uZXJLZXkiOiAiSW5uZXJWYWx1ZSJ9fX0=
  binary-value: YmFzZTY0IHN0cmluZwo=
stringData:
  list-value: |
    item1
    item2
    item3
  object-value: |
    key1: value1
    key2: value2
    key3: value3
  yaml-value: |
    key: value
    string: Hello, world!
    nested:
      innerKey: innerValue

It’s important to note that some values are in plaintext format, while some are in Base64-encoded format. Additionally, we specify the Base64-encoded values under the data field, whereas the plaintext-formatted values are available under the stringData field.

Moving on, let’s go ahead and create the my-secret Secret:

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

Lastly, we must understand that Kubernetes encodes the values under the stringData field before storing them under the data field. So, we need to access all the secret values using a path relative to the data field only:

$ kubectl get secret my-secret -o jsonpath='{.data.string-value}' | base64 -d
Hello, world!
$ kubectl get secret my-secret -o jsonpath='{.data.yaml-value}' | base64 -d
key: value
string: Hello, world!
nested:
  innerKey: innerValue

It’s interesting to note that, unlike ConfigMap values, we must explicitly decode the Secret values to see them in the plaintext format.

3. Environment Variables

Now that we’ve learned to create ConfigMap and Secret, we can inject them as environment variables into a Kubernetes pod. So, let’s go ahead and look into the pod.yaml manifest file that uses an ubuntu image to run inside the container:

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: ubuntu-container
      image: ubuntu
      command: ["sleep", "infinity"]
      env:
        - name: SECRET_VALUE
          valueFrom:
            secretKeyRef:
              name: my-secret
              key: string-value
        - name: CONFIG_VALUE
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: number-value

We must note that we used the secretKeyRef field to refer to the my-secret Secret, and configMapKeyRef to refer to the my-configmap ConfigMap. Further, we named the environment variables as SECRET_VALUE and CONFIG_VALUE.

Next, let’s apply the configuration to create the my-pod pod in the Kubernetes cluster:

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

Finally, let’s use the kubectl exec command to check the environment variables inside the my-pod:

$ kubectl exec my-pod -- env | grep -i VALUE
CONFIG_VALUE=42
SECRET_VALUE=Hello, world!

Great! The environment variables are defined as expected. However, it’s worth noting that *although the *string-value**Secret was defined in Base64 encoding, it’s decoded into plaintext format while populating the value for the SECRET_VALUE environment variable**.

4. Built-in Types and Usage

The Kubernetes Secret resource supports several in-built types, such as Opaque, service-account-token, tls, dockerconfigjson, and so on. On the other hand, ConfigMaps don’t have any specific types. Let’s go through some of these types and their usage in this section.

4.1. Opaque Secrets

We can use the Opaque Secrets for storing arbitrary sensitive information in a Kubernetes cluster. To understand the usage, let’s start by defining the mysql-secret to contain the credentials for a MySQL database:

$ cat mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  username: bXktcHJvdG9jb2xvcmVk
  password: bXktcGFzc3dvcmQ=
  root-password: bXktcm9vdC1wYXNzd29yZA==

Now, let’s define the mysql-pod.yaml manifest to hold the configuration for the mysql-pod:

$ cat mysql-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mysql-pod
spec:
  containers:
    - name: mysql-container
      image: mysql
      env:
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: username
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
      ports:
        - containerPort: 3306
  restartPolicy: Never

Next, let’s create the Secret and Pod resources using the mysql-secret.yaml and mysql-pod.yaml manifest files, respectively:

$ kubectl apply -f mysql-secret.yaml -f mysql-pod.yaml
secret/mysql-secret created
pod/mysql-pod created

Lastly, let’s validate that the MySQL server is up and running inside the mysql-pod pod:

$ kubectl exec -it mysql-pod -- mysql -uroot -pmy-root-password -e "SELECT VERSION();"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+
| VERSION() |
+-----------+
| 8.0.33    |
+-----------+

Great! We’ve got this one right.

4.2. tls Secrets

Now, let’s explore how to create tls secrets and use them inside the pods.

First, let’s generate the TLS private key in the tls.key file and certificate in the tls.crt file for demonstration purposes:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt

Now, let’s see how we can create the tls secret using the custom options –cert and –key:

$ kubectl create secret tls my-tls-secret --cert=tls.crt --key=tls.key
secret/my-tls-secret created

So, we must note that the kubectl create* command supports a few custom options for Secrets, which is not applicable for *ConfigMaps.

Next, let’s look at the manifest file for the tls-pod that’s going to mount the tls secret at the /etc/tls path:

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tls-pod
spec:
  containers:
    - name: ubuntu
      image: ubuntu
      command: ["sleep", "infinity"]
      volumeMounts:
        - name: tls-volume
          mountPath: /etc/tls
          readOnly: true

  volumes:
    - name: tls-volume
      secret:
        secretName: my-tls-secret

Moving on, let’s create the tls-pod using the pod.yaml manifest file:

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

Lastly, let’s verify that the secrets are available inside the container:

$ kubectl exec tls-pod -- ls /etc/tls/
tls.crt
tls.key

Perfect! We’ve got the relevant files inside the container for further use.

Based on our exploration, we can safely infer that compared to ConfigMaps, Secrets have a much broader scope, support, and usage within Kubernetes.

5. AutoMounting

Although we can explicitly mount both ConfigMaps and Secrets as volumes inside containers, only secrets of specific types, such as service-account-token, support the automounting feature. For this purpose, we need to specify the service account in the pod’s specification and set the automountServiceAccountToken field to true.

Let’s go ahead and look at the sa-ubuntu.yaml file that contains the configuration for the my-service-account service account and the ubuntu-pod pod:

$ cat sa-ubuntu.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account

---
apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-pod
spec:
  serviceAccountName: my-service-account
  containers:
    - name: ubuntu-container
      image: ubuntu
      command: ["sleep", "infinity"]
  automountServiceAccountToken: true

Next, let’s use this manifest to create the my-service-account and ubuntu-pod pod:

$ kubectl apply -f sa-ubuntu.yaml
serviceaccount/my-service-account created
pod/ubuntu-pod created

Lastly, we can verify that a token is automatically mounted at the /var/run/secrets/kubernetes.io/serviceaccount/token path:

$ kubectl exec ubuntu-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
# token is hidden

Fantastic! We’ve discovered one more feature of Kubernetes Secrets that is absent from ConfigMaps.

6. Feature Comparision

Now, we’ve developed a good understanding of Kubernetes ConfigMap and Secret. So, let’s use our understanding to draw a feature comparison summary between the two:

Comparison Criteria

ConfigMap

Secret

Data Classification

Stores non-sensitive information

Stores sensitive information

Data Encoding

Stores data in original format

Stores data in base64 format

Types and Usage

No specific type, and used for storing configuration data

Several in-built types, such as Opaque, tls, dockerconfigjson, each for a distinct usage

API Support

kubectl API provides generic support to interact with ConfigMap

kubectl API provides broader support for different types of Secret

Mounting as volumes

Only support explicit mounting

Supports automounting for service-account-token

7. Conclusion

In this article, we compared the features of Secrets and ConfigMaps in Kubernetes.

Furthermore, we explored how to create them, use them as environment variables, explicitly mount them, and enable automount for the service account tokens.