1. Introduction

ConfigMaps are one of the key resources in Kubernetes for managing configuration data. It’s important to have a smooth and convenient workflow for changing the configuration for application maintenance and upgrade.

In this tutorial, we’ll learn different ways to edit a ConfigMap using the kubectl command-line tool.

2. Scenario Setup

Our approach to editing ConfigMaps depends on how they were created. Let’s create a few sample ConfigMaps that we can reuse to understand different solutions.

2.1. ConfigMap From Environment Files

Let’s see two configuration files, namely, config1.env and config2.env, that contain key-value pair definitions:

$ cat config1.env
env1_var1=e1v1
env1_var2=e1v2
$ cat config2.env
env2_var1=e2v1
env2_var2=e2v2

Now, we can create the test-configmap1 ConfigMap using the kubectl create command:

$ kubectl create configmap test-configmap1
  --from-env-file=config1.env,config2.env --save-config
configmap/test-configmap1 created

We used the –from-env-file option to specify multiple configuration files simultaneously.

Next, we can use the kubectl get command to retrieve the contents of the test-configmap1 ConfigMap:

$ kubectl get configmap test-configmap1 -o json | jq '.data'
{
  "env1_var1": "e1v1",
  "env1_var2": "e1v2",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}

Since, we used the -o to output the content in JSON format, we could pipe the output to jq and retrieve the contents at the .data path.

2.2. ConfigMap From Files and Literal Values

We can also create ConfigMaps from files and literal values. However, we can’t use the –from-env-file option with this approach.

Let’s make some configuration files – config3.properties and config4.properties:

$ cat config3.properties
env3_var1=e3v1
env3_var2=e3v2
$ cat config4.properties
env4_var1=e4v1
env4_var2=e4v2

Although the format for key-value pairs in these files looks similar to config1.env and config2.env files, they aren’t stored in the same format within ConfigMap.

For this scenario, we can use the –from-file and –from-literal options while creating the test-configmap2 ConfigMap:

$ kubectl create configmap test-configmap2
  --from-file=cf3_props=config3.properties
  --from-file=config4.properties
  --from-literal=var1=v1 --from-literal=var2=v2 --save-config
configmap/test-configmap2 created

We must note that k8s uses the file basename as the key when we don’t specify one. Additionally, we must define each literal key-value pair separately with the –from-literal option.

Let’s verify the contents of the test-configmap2 ConfigMap:

$ kubectl get configmap test-configmap2 -o json | jq '.data'
{
  "cf3_props": "env3_var1=e3v1\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

It’s important to note that when we use the –from-file option, the entire file content is mapped to a single key.

Finally, let’s imagine that we made a few edits to the config1.env and config3.properties configuration files:

$ cat config1.env
env1_var1=e1v1-modified
env1_var3=e1v3
% cat config3.properties
env3_var1=e3v1-modified
env3_var2=e3v2

We want these changes to be reflected in the corresponding ConfigMaps.

For each of the following sections, we’ll assume that we are working with newly created versions of the ConfigMaps where we want to incorporate the configuration changes.

3. Using kubectl delete and kubectl create

We can view an edit operation to a ConfigMap as a sequence of delete and create operations.

For this approach, let’s use the kubectl delete command to delete the test-configmap1 and test-configmap2 ConfigMaps:

$ kubectl delete configmap test-configmap1 test-configmap2
configmap "test-configmap1" deleted
configmap "test-configmap2" deleted

Now, we can use the kubectl create command to recreate the two ConfigMaps and incorporate the changes made to configuration files:

$ kubectl create configmap test-configmap1 --from-env-file=config1.env,config2.env --save-config
configmap/test-configmap1 created
$ kubectl create configmap test-configmap2
  --from-file=cf3_props=config3.properties
  --from-file=config4.properties
  --from-literal=var1=v1 --from-literal=var2=v2 --save-config
configmap/test-configmap2 created

Let’s verify the contents of the newly created ConfigMaps by retrieving their contents in one go:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json
  | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

Although we’ve incorporated the edits to the ConfigMaps conveniently, this approach can cause downtime for time-sensitive applications.

4. Using kubectl edit

When we’re comfortable making the configuration changes manually, we can use the kubectl edit command to edit the ConfigMaps resources directly.

Let’s try it out by editing the test-configmap1 and test-configmap2 ConfigMaps:

$ kubectl edit configmap test-configmap1 test-configmap2

It’ll open the resource files in the default editor, such as Vim:

kubectl edit configmap

We can make the necessary changes and save the changes with the :wq command.

Further, let’s verify the latest changes by using the kubectl get command:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

The content looks as expected.

5. Using kubectl apply

Now, let’s learn how to use the kubectl apply command to edit existing ConfigMaps seamlessly.

5.1. With the –dry-run Option

When the configuration files are available locally, we can change them directly. Further, we can use the kubectl create command with the –dry-run and -o options to get the ConfigMap resource file and pipe the output to the kubectl apply command.

Let’s try this approach with the test-configmap1 ConfigMap:

$ kubectl create configmap test-configmap1
  --from-env-file=config1.env,config2.env --dry-run=client -o yaml
  | kubectl apply -f - 
configmap/test-configmap1 configured

We used  -f option with kubectl apply to read the resource file from a file. Additionally, we specified (stdin) so that it takes the output from the previous command in the pipe.

Similarly, we can make changes to the test-configmap2 ConfigMap:

$ kubectl create configmap test-configmap2
  --from-file=cf3_props=config3.properties
  --from-file=config4.properties
  --from-literal=var1=v1 --from-literal=var2=v2 --dry-run=client -o yaml
  | kubectl apply -f -
configmap/test-configmap2 configured

Finally, we can verify the changes to the two ConfigMaps:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

As we can see, our approach worked as expected.

5.2. With Resource File

In most scenarios, the configuration files might not be available locally. So, we need to use the definition from the existing ConfigMaps directly.

First, let’s create the configmaps.json resource definition file for both the ConfigMaps:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json > configmaps.json

Next, let’s make the necessary changes to the key-value pairs within the configmaps.json resource file:

$ cat configmaps.json | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

We’ve used jq to show only the data field from each of the ConfigMaps.

Finally, we can apply these changes with kubectl apply command:

$ kubectl apply -f configmaps.json
configmap/test-configmap1 configured
configmap/test-configmap2 configured

As we can see, we’ve edited both ConfigMaps successfully.

6. Using kubectl replace

Alternatively, when we’ve got a modified resource file definition for the ConfigMap resource files, we can use the kubectl replace command to replace the existing ConfigMaps.

Let’s see this in action by using the recently modified configmaps.json file:

$ kubectl replace -f configmaps.json
configmap/test-configmap1 replaced
configmap/test-configmap2 replaced

The output message suggests that we’ve replaced the test-configmap1 and test-configmap2 ConfigMaps.

As earlier, we must verify that the latest changes are reflected in the ConfigMaps:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

As we can see, this has worked.

7. Using kubectl patch

While we can use kubectl replace to replace the entire ConfigMap, we can use the kubectl patch command to update specific fields.

Let’s create the config1.env-patch patch file to include the incremental changes to test-configmap1:

$ cat config1.env-patch
{"data":{"env1_var1":"e1v1-modified", "env1_var3":"e1v3"}}

We must note that we don’t have an entry corresponding to the env1_var2 key. Additionally, we’ve got a new key, env1_var3.

Now, let’s use kubectl patch with –patch-file option to apply the selected changes:

$ kubectl patch configmap test-configmap1 --patch-file=config1.env-patch
configmap/test-configmap1 patched

Similarly, we can create the config3.properties-patch patch file for the test-configmap2 ConfigMap:

$ cat config3.properties-patch
{"data":{"cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n"}}

It’s important to note that the entire content of config3.properties is mapped to the cf3_props key, so we need to include the entire value in the patch.

Next, let’s patch test-configmap2 using the patch file:

$ kubectl patch configmap test-configmap2 --patch-file=config3.properties-patch
configmap/test-configmap2 patched

Finally, let’s verify the latest version of the ConfigMaps:

$ kubectl get configmap test-configmap1  -o json test-configmap2 -o json | jq '.items[].data'
{
  "env1_var1": "e1v1-modified",
  "env1_var2": "e1v2",
  "env1_var3": "e1v3",
  "env2_var1": "e2v1",
  "env2_var2": "e2v2"
}
{
  "cf3_props": "env3_var1=e3v1-modified\nenv3_var2=e3v2\n",
  "config4.properties": "env4_var1=e4v1\nenv4_var2=e4v2\n",
  "var1": "v1",
  "var2": "v2"
}

We can still see the env1_var2 key in test-configmap1 because it wasn’t included in the patch file. So, the changes look as expected.

8. Conclusion

In this article, we covered several methods to edit ConfigMaps using the kubectl utility.

We explored the kubectl delete with kubectl create approach for rebuilding ConfigMaps from scratch, and the kubectl edit approach for direct manual changes. Additionally, we learned to use kubectl apply for seamless updates, kubectl replace for complete overwrites, and kubectl patch for selective incremental updates.

Each method offers unique advantages based on the need for simplicity, precision, or minimal downtime.