Hello Devs! Welcome back to our series of stories with Kubernetes and Spring ecosystem. In this post, we’ll learn how to use ConfigMaps in Kubernetes.
· Overview
∘ What are Kubernetes ConfigMaps?
∘ Where are ConfigMaps stored in Kubernetes?
∘ ConfigMap object
· How to use a ConfigMap to configure a container inside a Pod
∘ 1. Environment variables for a container
∘ 2. Injecting a ConfigMap entry as a command-line argument
∘ 3. Mounting ConfigMaps into Pods as volumes
∘ 4. Using Immutable ConfigMaps
· Conclusion
· References
This series of stories shows how to use Kubernetes in the Spring ecosystem. We work with a Spring boot API and Minikube to have a lightweight and fast development environment similar to production.
- Lab1 (Spring Boot/K8S): Deploy Spring Boot application on Kubernetes
- Lab2 (Spring Boot/K8S): Kubernetes health probes with Spring Boot
- 👉 Lab3 (Spring Boot/K8S): Mastering ConfigMaps in Kubernetes
In the previous lab (lab 2), we saw how to use Spring’s Actuator to improve our application’s health monitoring with Kubernetes. In this story, we’ll learn how to configure the spring boot application on Kubernetes to use ConfigMaps.
Overview
What are Kubernetes ConfigMaps?
A Kubernetes ConfigMap is an API object used to store non-confidential data in key-value pairs. A ConfigMap is not designed to hold large chunks of data. The data stored in a ConfigMap cannot exceed 1 MiB. If you need to store settings that are larger than this limit, you may want to consider mounting a volume or using a separate database or file service.
Caution: ConfigMap does not provide secrecy or encryption. If the data you want to store are confidential, use a Secret rather than a ConfigMap, or use additional (third party) tools to keep your data private.
Where are ConfigMaps stored in Kubernetes?
Kubernetes stores ConfigMaps in its etcd datastore. You shouldn’t directly edit etcd data; instead, use the Kubernetes API server and Kubectl to create and manage the ConfigMaps.
ConfigMap object
A ConfigMap is an API object that lets you store configuration for other objects to use. Unlike most Kubernetes objects that have a spec, a ConfigMap as data and binaryData fields. These fields accept key-value pairs as their values. Both the data field and the binaryData are optional. The data field is designed to contain UTF-8 strings while the binaryData field is designed to contain binary data as base64-encoded strings.
Each key under the data or the binaryData field must consist of alphanumeric characters, -, _ or .. The keys stored in data must not overlap with the keys in the binaryData field.
Sample 1
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
# property-like keys; each key maps to a simple value
host: "192.168.10.1"
debug_mode: "1"
database: demodb
# file-like keys
app.properties: |
server.port=8080
api.key=uolfbghndkks
Sample 2
apiVersion: v1
kind: ConfigMap
metadata:
name: security-config
binaryData:
secret_key: YQhIHKR+C1MpSgiOZcThDwbagd+JCeDyKYP8hl4bGA3nh1U7RI51aKKrwt/7Ui3IhDVSi/R69QLJRNWk/UqOHg==
How to use a ConfigMap to configure a container inside a Pod
Let’s start by cloning the main Spring Boot project with the previous labs.
$ git clone https://github.com/anicetkeric/spring-boot-k8s.git -b main
There are four different ways that you can use a ConfigMap to configure a container inside a Pod:
- Environment variables for a container
- Inside a container command and args
- Add a file in read-only volume, for the application to read
- Using Immutable ConfigMaps
1. Environment variables for a container
In the following deployment manifest, all data in the db-config ConfigMap are available as environment variables:
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
# property-like keys; each key maps to a simple value
host: "192.168.10.1"
debug_mode: "1"
database: demodb
---
apiVersion: v1
kind: Service
metadata:
name: book-api-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-book-api
spec:
replicas: 2
selector:
matchLabels:
app: backend
environment: dev
template:
metadata:
labels:
app: backend
environment: dev
spec:
containers:
- name: book-api
image: spring-boot-k8s:latest
ports:
- containerPort: 8080
imagePullPolicy: Never
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
envFrom: # Use envFrom to load ConfigMaps into environment variables.
- configMapRef:
name: db-config # refers to a ConfigMap by its name
The envFrom field instructs Kubernetes to create environment variables from the sources nested within it. The configMapRef refers to a ConfigMap by its name and selects all its key-value pairs.
Sometimes a Pod does not require access to all the values contained in a ConfigMap. Let’s say our pod only needs host and database variables.
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
# property-like keys; each key maps to a simple value
host: "192.168.10.1"
debug_mode: "1"
database: demodb
---
apiVersion: v1
kind: Service
metadata:
name: book-api-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-book-api
spec:
replicas: 2
selector:
matchLabels:
app: backend
environment: dev
template:
metadata:
labels:
app: backend
environment: dev
spec:
containers:
- name: book-api
image: spring-boot-k8s:latest
ports:
- containerPort: 8080
imagePullPolicy: Never
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
env: # array of environment variable definitions
- name: host
valueFrom: # select individual keys in a ConfigMap
configMapKeyRef:
name: db-config # refers to a ConfigMap by its name
key: host
- name: database
valueFrom:
configMapKeyRef:
name: db-config
key: database
In that manifest, we can see two environment variables. The env field is an array of environment variable definitions.
The env.valueFrom syntax can be used instead of envFrom for this use case. It lets you select individual keys in a ConfigMap. Keys can also be renamed to different environment variables.
Let’s save the manifest to k8s-configmap.yaml, then use Kubectl to apply it to the cluster and create the ConfigMap object:
$ kubectl apply -f k8s-configmap.yaml
configmap/db-config created
service/book-api-service created
deployment.apps/backend-book-api created
$ kubectl describe configmap db-config
Name: db-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
database:
----
demodb
debug_mode:
----
1
host:
----
192.168.10.1
BinaryData
====
Events: <none>
We’ll create a new controller in the Spring Boot application to get configMaps entries.
record DbInfo(String host, String name, String debug) {}
@RestController
@RequestMapping("/api")
public class ConfigMapController {
@Value("${application.env.db-host}")
private String host;
@Value("${application.env.db-name}")
private String name;
@Value("${application.env.debug}")
private String debug;
@GetMapping("/db-config")
public DbInfo getStatus() {
return new DbInfo(host, name, debug);
}
}
In application.yml file:
application:
env:
db-host: ${host:N/A}
db-name: ${database:N/A}
debug: ${debug_mode:0}

2. Injecting a ConfigMap entry as a command-line argument
ConfigMap values can be interpolated into a container’s command line arguments by first referencing the relevant ConfigMap key as an environment variable.
For use ConfigMap entries in the pod.spec.containers.args field, we need first initialize an environment variable from the ConfigMap entry and then refer to the variable inside the arguments.
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
# property-like keys; each key maps to a simple value
host: "192.168.10.1"
debug_mode: "1"
database: demodb
profile: stage
---
apiVersion: v1
kind: Service
metadata:
name: book-api-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-book-api
spec:
replicas: 2
selector:
matchLabels:
app: backend
environment: dev
template:
metadata:
labels:
app: backend
environment: dev
spec:
containers:
- name: book-api
image: spring-boot-k8s:latest
ports:
- containerPort: 8080
imagePullPolicy: Never
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
args: ["--spring.profiles.active=$(APP_PROFILE)"]
env:
- name: APP_PROFILE
valueFrom:
configMapKeyRef:
name: db-config
key: profile
Adding an args attribute to the container will make Kubernetes run the Docker image with the configured value, exactly just as when we started it manually above.
When we look at the startup logs we can see that the active profile has taken the value specified in the configMaps.

3. Mounting ConfigMaps into Pods as volumes
ConfigMaps can be mounted as data volumes. ConfigMaps can also be used by other parts of the system, without being directly exposed to the Pod.
To set up a volume mount, add an entry to your Pod manifest’s spec.volumes field that uses a configMap entry to reference your ConfigMap by name. Then, use the volumeMounts field to mount the volume to a read-only path in the container.
This is an example of a Pod that mounts a ConfigMap in a volume:
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
# property-like keys; each key maps to a simple value
host: "192.168.10.1"
debug_mode: "1"
database: demodb
profile: stage
# file-like keys
app.properties: |
server.port=8080
api.key=uolfbghndkks
---
apiVersion: v1
kind: Service
metadata:
name: book-api-service
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 8081
targetPort: 8080
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-book-api
spec:
replicas: 2
selector:
matchLabels:
app: backend
environment: dev
template:
metadata:
labels:
app: backend
environment: dev
spec:
containers:
- name: book-api
image: spring-boot-k8s:latest
ports:
- containerPort: 8080
imagePullPolicy: Never
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 1
volumeMounts:
- name: config-volume
mountPath: /etc/db/
volumes:
- name: config-volume
configMap:
name: db-config
If there are multiple containers in the Pod, then each container needs its ownvolumeMounts block, but only one .spec.volumes field is needed per ConfigMap.
Now, the application can access the app.propertiesfile at the specified mount path /etc/db .
The app.properties file is in the /etc/db folder inside the pod.

We added a new method to ConfigMapController to read the contents of the app.properties file.
@GetMapping("/db-config-volume")
public List<String> getVolumeFileContent() {
String fileName = "/etc/db/app.properties";
try{
return Files.readAllLines(Paths.get(fileName),
StandardCharsets.UTF_8);
}catch (IOException ex){
return Collections.emptyList();
}
}

4. Using Immutable ConfigMaps
Immutable ConfigMaps are ConfigMaps in Kubernetes that have been marked asimmutable, meaning their data cannot be modified after creation. Once a ConfigMap is marked as immutable, any attempts to update its data will be rejected by Kubernetes.
This enhances safety by preventing accidental modification or deletion of ConfigMap keys that your app depends on.
To create an immutable ConfigMap, set its immutable manifest property to true:
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
ip: "192.168.10.1"
immutable: true
Once a ConfigMap is marked as immutable, it is impossible to revert this change nor to mutate the contents of the data or the binaryData field. We can only delete and recreate the ConfigMap. Because existing Pods maintain a mount point to the deleted ConfigMap, it is recommended to recreate these pods.
Conclusion
Well done !!. In this post, we have learned how to configure the spring boot application on Kubernetes to use ConfigMaps
The complete source code of this series is available on GitHub.
You can reach out to me and follow me on Medium, Twitter, GitHub, Linkedln
Support me through GitHub Sponsors.
Thanks for reading!