Kubernetes (K8s) is an open-source platform that efficiently and automatically orchestrates the deployment of containerized applications. Its containerized features allow you to easily scale up and down your application and decouple it from other dependencies.
A container is a small unit that hosts your application and all of its dependencies; this ensures your application can be easily recreated if the container goes down or experiences any issues, increasing portability. However, an orchestration tool such as Kubernetes is required to remove the manual overhead associated with managing containers.
Nonetheless, the Kubernetes ecosystem offers key functionalities such as self-healing capabilities, load balancing, declarative configuration with YAML, seamless rollouts, and the ability to enable organizations to be resilient with their application architecture.
What we will cover:
Kubernetes functions on its resources, which are the building blocks used to define, configure, and manage the containerized application in the Kubernetes cluster. Resources include Pods, Deployment, Replica Sets, Config Maps, Secrets, Ingress, Persistent Volumes, Persistent Volume Claims, Services, Jobs/CronJobs, and more.
The resources enable users to specify exactly how they want their application to behave, interact, and scale within the environment. They are written in YAML format and managed by the Kubernetes API server. They ensure application deployments and configurations are consistent across environments such as development, testing, and production.
Resource configurations and deployments are important in properly managing your Kubernetes environment, but there are times when we need to access the resource and make some changes, and it can become a bit tricky to keep the resource up and running. kubectl patch
is a command that is used to update existing resources in your Kubernetes cluster without needing to fully replace or redeploy them.
Let’s explore the different ways we can utilize the kubectl patch
command for our benefit.
kubectl patch
is a Kubernetes command that allows you to edit your existing Kubernetes resources without disrupting the running services and preventing you from recreating your YAML file. This process reduces the risk of accidentally overwriting or deleting other parts of the configuration.
With kubectl patch
, you can quickly fix issues with updating the name, image, label, replicas, affinity/tolerations, environment variables, configaps, secrets, volumes, ports, etc.
kubectl patch
supports YAML and JSON formats. Using either format, you can drill into the specific field of the resource and change it to your desired value. Kubectl supports three different patching strategies: Strategic Merge (the default), JSON, and JSON Merge. You utilize these formats through the patching strategy.
The following is the general syntax of the kubectl patch
command. By default, it is using the Strategic Merge patch type:
kubectl patch <resource-type> <resource-name> --patch '<patch-data>'
Example: Updating fields in Kubernetes deployment file using kubectl patch
Let’s say we have the following Nginx deployment YAML file, we originally had only one label (environment: prod
), and we want to add a new label to specify the app we are running (app: nginx
). We will use the --patch
command to enter the new labels:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
environment: prod
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
environment: prod
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
We would run the following command to add the new labels:
kubectl patch deployment nginx-deployment --patch '{"metadata": {"labels": {"app": "nginx"}}}'
For the patch command to work, you must correctly include the map and lists in the original YAML file, so Kubernetes knows exactly where to go to add/update the new value properly.
In this same scenario, if we wanted to update the labels within the Pod template, you would have to drill into the spec
field instead of the metadata.
Usually, you would drill into the metadata
and spec
fields for kubectl patch
, but this would also work with other YAML fields, such as status
or even a new custom field you created through a CRD (custom resource definition).
In the following example, we are attempting to add a new label app: nginx
to the existing labels within the pod template, as the following:
kubectl patch deployment nginx-deployment --patch '{"spec": {"template": {"metadata": {"labels": {"app": "nginx"}}}}}'
The same works if you want to update an existing label:
kubectl patch deployment nginx-deployment --patch '{"spec": {"template": {"metadata": {"labels": {"environment": "dev"}}}}}'
Once the fields have been updated, you can run a kubectl get <resource><name>
to verify if the changes went through successfully.
To check if the deployment got the new labels, you can run the following:
kubectl get deployment nginx-deployment --show-labels
To check if the Pod received the new labels, you can run the following:
kubectl get pods --selector=app=nginx --show-labels
kubectl get pods --selector=environment=dev --show-labels
This was a simple example of using the kubectl patch
command to update fields in your Kubernetes deployment file.
Is using kubectl patch a good practice?
kubectl patch
is great for quick, targeted changes and avoids overwriting entire resources. However, it lacks auditability, can cause YAML drift in GitOps workflows, and isn’t ideal for complex updates. You can use it for small updates, but you should rely on declarative methods for consistent management.
As mentioned above, Kubernetes supports three types of patching strategies: JSON Patch, JSON Merge Patch, and Strategic Merge Patch.
Let’s look at them in more detail.
Strategic Merge Patch (default)
The Strategic Merge Patch works on identifying the arrays and maps in the Kubernetes resources and merging the changes at the main field level.
This patch type can scan the structure of the Kubernetes resource and apply the changes properly without affecting the existing fields and focusing only on the fields you specified to update. It is effective for updating fields such as lists and maps.
We will use the following YAML for our Strategic Merge Patch examples:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 1. Update image container
We will attempt to update the image of the apache-container
from httpd:2.4
to httpd:2.4.62
. This will require us to drill further into the list (the -
is a list and will use [ ]
to tap into it from the kubectl patch
command) instead of a map (uses { }
):
kubectl patch deployment apache-deployment --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4.62"}]}}}}'
You can run the following to check the YAML for the new updates:
kubectl get deployment apache-deployment -o yaml
In return, we get the following YAML file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62 ##UPDATED
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 2. Update Pod label and container image
We can also perform multiple updates at the same time. For example, if we want to add a new label to the Pod for the environment: prod
and update the image to httpd:2.4.62
, we can run the following:
kubectl patch deployment apache-deployment --patch '{"spec": {"template": {"metadata": {"labels": {"environment": "production"}}, "spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4.62"}]}}}}'
In return, we get the following file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
environment: production ##ADDED
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62 ##UPDATED
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 3. Add a Node Selector
The Node Selector will allow us to schedule these pods on Nodes that have the label zone:centralus
, giving us more control over the placement of our resources:
kubectl patch deployment apache-deployment --patch '{"spec": {"template": {"spec": {"nodeSelector": {"zone": "centralus"}}}}}'
This is the returning file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
nodeSelector: ##ADDED
zone: centralus
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 4. Update resource requests and limits
kubectl patch
command is also useful in scenarios where you want to update the resources you allocate to your applications through resource requests and limits.
Here we can quickly add or update the specs without any downtime:
kubectl patch deployment apache-deployment --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "resources": {"requests": {"cpu": "500m", "memory": "256Mi"}, "limits": {"cpu": "1", "memory": "512Mi"}}}]}}}}'
In return, we get the following YAML file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
resources: ##ADDED
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 1
memory: 512Mi
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 5. Add Tolerations
Through Taints and Tolerations, we can also avoid placing our pods in specific Nodes. This allows us to reserve certain nodes for specific workloads or prevent pods from being scheduled on nodes.
Here, we want to prevent our Apache application from being scheduled on any nodes dedicated to Nginx.
kubectl patch deployment apache-deployment --patch '{"spec": {"template": {"spec": {"tolerations": [{"key": "app", "operator": "Equal", "value": "nginx", "effect": "NoSchedule"}]}}}}'
The output YAML file we get:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
tolerations: ##ADDED
- key: app
operator: Equal
value: nginx
effect: NoSchedule
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Overall, the Strategic Merge Patch method is a straightforward way to add or update fields within your Kubernetes workloads without experiencing application downtime or having to fully re-deploy your application in the cluster.
It works best for managing containers because it recognizes the keys (the name for containers) and merges the changes based on those keys.
JSON Merge Patch
The JSON Merge Patch operates at the JSON Object Level and is a simpler patching strategy than the Strategic Merge Patch method. However, it does not understand Kubernetes’ specific structure, such as keys in lists (the name for containers), as the Strategic Merge Patch method does.
JSON Merge Patch method fully replaces the fields you want to update instead of merging them. This means that every time you want to update one list item using the JSON Merge Patch method, you will need to write out all of the items on that specific list, or else the unlisted items will be removed.
The JSON Merge Patch method is used mostly for overwriting and deleting fields, fully replacing lists, and performing quick updates on your Kubernetes resources. It also works with Kubernetes native resources and custom resource definitions (CRDs), offering flexibility and ease of use for different resource management scenarios.
We will continue with the same YAML example to demonstrate various ways of using the JSON Merge Patch method.
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 1. Update container image
This command is similar to the one we would have used for patching using the Strategic Merge Patch Method, but it uses the flag type=merge
.
kubectl patch deployment apache-deployment --type=merge --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4.62"}]}}}}'
It’s important to understand how the JSON Merge Patch method differs from the Strategic Merge Patch method. The JSON Merge Patch replaces the entire list; in this example, the command would overwrite the entire container list. However, the Strategic Merge Patch method merges the list intelligently, as it knows how to identify the container list through its keys and names.
In this specific example, the result of this command would update the image version. However, this would also remove the sidecar container from the list of containers since we did not include it in the command:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62 ##UPDATED
ports:
- containerPort: 80
##Sidecar Container REMOVED
Example 2. Add a new environment variable to the container
We will now add a new environment variable to the apache-container
, but we don’t want to remove the sidecar container. We will need to specify the sidecar container in the patch command:
kubectl patch deployment apache-deployment --type=merge --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4", "env": [{"name": "FOO_API_INSTANCE", "value": "127.0.0.1:3245"}]}, {"name": "sidecar", "image": "fluent/fluent-bit:1.9.5"}]}}}}'
This will result in the following YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
env:
- name: FOO_API_INSTANCE
value: 127.0.0.1:3245
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 3. Delete a field
The JSON Merge Patch can easily remove a field from your YAML file by setting that field to null
.
For example, if we want to remove the annotation we have set for team: devops
, we can simply set the team
key to null
and that would automatically delete that field:
kubectl patch deployment apache-deployment --type=merge --patch '{"metadata": {"annotations": {"team": null}}}'
This gives us the following file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Deleting specific fields across your YAML is useful with JSON Merge Patch. However, when it comes to deleting list items, it’s better to write out the entire list without the specific list item that you want to remove since JSON Merge Patch replaces the entire list.
For example, say we have an environment variable we specified in the Apache container, and we want to remove it:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
env:
- name: FOO_API_INSTANCE
value: 127.0.0.1:3245
image: httpd:2.4
- name: sidecar
image: fluent/fluent-bit:1.9.5
Run the following JSON Merge Patch command:
kubectl patch deployment apache-deployment --type=merge --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4"}, {"name": "sidecar", "image": "fluent/fluent-bit:1.9.5"}]}}}}'
We then get the following file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 4. Clearing resource limits
The JSON Merge Patch can be useful when it comes to fully removing any resource limits you may have placed previously. You can easily set limits
to ‘null’ to remove all cpu
or memory
limits you have.
For example, say you have the following CPU and memory limits in place, and you want to remove them:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 1
memory: 512Mi
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Run the following JSON Merge Patch command to remove the resource limits:
kubectl patch deployment apache-deployment --type=merge --patch '{"spec":{"template":{"spec":{"containers":[{"name":"apache-container","image":"httpd:2.4","resources":{"requests":{"cpu":"500m","memory":"256Mi"},"limits":null},"ports":[{"containerPort":80}]},{"name":"sidecar","image":"fluent/fluent-bit:1.9.5"}]}}}}'
In return, we get the following YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
resources:
requests:
cpu: 500m
memory: 256Mi
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
The JSON Merge Patch method allows you to redefine fields and lists. This is powerful when replacing or deleting the entire configuration in a list. However, it’s very important to explicitly list all the items in your list to avoid accidental deletion.
JSON Patch (RFC 6902)
The JSON Patch method is based on RFC 6902, which gives you more fine-grained control over the modifications you want to make to your Kubernetes resources. This method uses specific operations to perform its changes. The operations include add, remove, move, replace, test, and copy. In the patching command, you will need to specify the type of patch to JSON (type=json
).
The JSON Patch method is used primarily to perform complex updates; however, its unique operations can also be used to make simple changes. Unlike the JSON Merge Patch method, you can drill into a list and replace the specific item you want to replace without affecting the entire list.
This method allows you to precisely control deletions, perform complex list modifications, perform conditional updates with testing, prevent accidental overwrites, and perform multiple operations (adding, replacing, and removing fields) within the same command. It is also easier to understand when typing it out and less error-prone in terms of syntax.
Here is a list of all the key operations JSON Patch can perform:
- add: Adds a new field or new list item
- remove: Deletes a specific field or list item
- replace: Replaces a field value
- move: Moves a field from one location to another
- copy: Copies a field to another location
- test: Test if the specific field has a value before making the changes
We will continue to use the same YAML file to run our JSON Patch commands on:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 1. Update the image
To update the image version to another version, we will have to use the replace
operation with the specific container image path:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "httpd:2.4.62"}]'
This will result in the following YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62 ##UPDATED
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 2. Add new port to the container
In the following example, we will attempt to add a new container port to the existing list of ports. With JSON Patch, adding new items to the list of ports is much more precise and direct.
The JSON Patch method focuses on directly adding the port to the list instead of replacing the entire ports field or overwriting the entire list of ports. This route is much cleaner and safer if you want to avoid accidentally removing existing ports. To add a new port, we will have to utilize the ‘add’ operation against the path of the container ports.
This is a list, so we have to address it through its position index number in the path.
In this example, we want to add the new container port to the first container, which has a position index number of 0
. To drill into the sub-list of ports and add to it, we need to add -
to the end of the path as the following:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "add", "path": "/spec/template/spec/containers/0/ports/-", "value": {"containerPort": 443}}]'
Here’s the returning YAML file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62
ports:
- containerPort: 80
- containerPort: 443 ##UPDATED
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 3. Deleting an Item from a list
In this example, we will attempt to delete the container port we added in the previous step. To do so, we will use the remove
command with the path for the specific container port we want to delete.
To drill into the 443
container port and delete it, we have to address it through its position index number in the path, as follows:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "remove", "path": "/spec/template/spec/containers/0/ports/1"}]'
Example 4. Conditionally replacing a field with test
The test
operation allows us to first check if the path exists before applying any other operation to it. This is useful to avoid getting an error when running a full patch operation command from the beginning.
We will first test the location in the deployment to see if an annotation of team:value
exists. If this exists, we will attempt to replace the team’s value to platform
.
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "test", "path": "/metadata/annotations/team", "value": "devops"}, {"op": "replace", "path": "/metadata/annotations/team", "value": "platform"}]'
In return, we will get the following YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: platform ##REPLACED
spec:
containers:
- name: apache-container
image: httpd:2.4
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
Example 5. Move container port from one container to another
In the following example, we will use the move
operator to remove the 443 port from our Apache Container and add it to the Sidecar Container as a new port.
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62
ports:
- containerPort: 80
- containerPort: 443
- name: sidecar
image: fluent/fluent-bit:1.9.5
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "move", "from": "/spec/template/spec/containers/0/ports/1", "path": "/spec/template/spec/containers/1/ports"}]'
This will give us the following result:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62
ports:
- containerPort: 80
- name: sidecar
image: fluent/fluent-bit:1.9.5
ports:
- containerPort: 443 ##MOVED
Example 6. Copy the container port to another container
In situations where we want to simply copy all the container ports from one container to another instead of fully moving it over, we can use the copy
operation:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "copy", "from": "/spec/template/spec/containers/0/ports", "path": "/spec/template/spec/containers/1/ports"}]'
This will give us the following YAML result:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62
ports:
- containerPort: 80
- containerPort: 443
- name: sidecar
image: fluent/fluent-bit:1.9.5
ports: ##COPIED
- containerPort: 80
- containerPort: 443
JSON Patch method gives you granular control over editing certain fields in your Kubernetes resources. Unlike the other patching methods, you can use specific operations to handle complex updates and modifications without affecting the existing fields.
Overall, JSON Patch is useful for performing field-level operations such as adjusting resource configurations, conditional modifications, updating environment variables, or quickly deleting fields from your resource.
The kubectl patch
and kubectl apply
commands are similar in many ways, but they serve different purposes in Kubernetes.
kubectl patch
is ideal for making specific, ad-hoc changes to existing Kubernetes resources. Methods like Strategic Merge Patch, JSON Merge Patch, and JSON Patch allow granular modifications without altering underlying fields.
However, integrating kubectl patch
into CI/CD workflows or GitOps practices can be challenging. It’s better suited to on-the-fly updates, as it doesn’t modify the YAML/JSON manifest or track changes.
By contrast, kubectl apply
deploys resources from a YAML/JSON manifest and maintains the desired state. A common workflow involves using kubectl apply
for initial deployments and kubectl patch for temporary modifications. This approach risks losing track of changes over time.
kubectl apply
ensures resources match the manifest, making it ideal for CI/CD automation to enforce consistency and a single source of truth. However, it resets or removes fields not defined in the manifest, so understanding your environment’s resource management is crucial when choosing between the two methods.
Example: kubectl patch command in a CI/CD pipeline
If the team is utilizing version control and CI/CD pipelines, for example, it is probably cleaner and safer to apply changes to your YAML/JSON manifest file and use kubectl apply
in future.
If various members of your team are continuously making changes (adding labels, updating image versions, adding CPU/Memory limits, etc.) on top of the original resource deployment through patching commands, it is safer to utilize the kubectl patch
command in the future to avoid any unwanted changes.
Running a kubectl apply
on top of all, the ad-hoc changes will remove/reset most of the changes made by the team and bring the resource back to the original state laid out in the YAML/JSON manifest.
For clarity, if we were to use this Initial YAML file for our application deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
We can update the image version through kubectl patch
by running the following:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "httpd:2.4.62"}]'
Or we can edit the original YAML, update the image version and run kubectl apply
after:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4.62 ##EDITED
kubectl apply -f apache-deployment.yaml
We would receive the same results in terms of the pod rollout
: Both kubectl patch
and kubectl apply
would bring down the original pods in the deployment and create new ones from this change.
NAME READY STATUS RESTARTS AGE
apache-deployment-76959867c-6spcs 1/1 Terminating 0 20s
apache-deployment-76959867c-gqwqt 1/1 Terminating 0 20s
apache-deployment-7f4897d967-9mwds 1/1 Running 0 2s
apache-deployment-7f4897d967-mz8q4 1/1 Running 0 4s
If we did not edit the original YAML manifest file and just did the image version change through kubectl patch
and ran kubectl apply
on top of this change:
kubectl apply -f apache-deployment.yaml
We would end up going back to the original deployment with an image version of httpd:2.4
:
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
template:
metadata:
labels:
app: apache
spec:
containers:
- name: apache-container
image: httpd:2.4
To summarize, the kubectl patch is best used for granular ad-hoc changes to your kubernetes resource, and kubectl apply is encouraged to be used in version-controlled or CI/CD environments that require full resource reconciliation to maintain consistency and having a single source of truth.
kubectl edit
allows you to modify live Kubernetes resources manually, providing full visibility into the entire resource. It’s ideal for inspecting configurations and making multiple changes at once, such as adding labels, updating image versions, or editing annotations. However, this method is less structured and better suited for exploratory changes or debugging.
kubectl patch
, on the other hand, offers precise, programmatic updates to specific fields without editing the entire resource, reducing the risk of errors from manual edits.
Use kubectl edit
for multiple, holistic changes and real-time verification while kubectl patch
is better for safe, granular updates. Both serve distinct purposes: kubectl edit
for flexibility and kubectl patch
for precision.
Example: Making multiple changes on the Kubernetes resource
In the following example, we use kubectl edit
to make multiple changes on the Kubernetes resource and run the kubectl patch
command to make those same exact changes to demonstrate the difference in the editing approach.
The original YAML file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-deployment
labels:
app: apache
spec:
replicas: 2
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
annotations:
team: devops
spec:
containers:
- name: apache-container
image: httpd:2.4
Use kubectl edit
to modify the image version and add a new annotation:
kubectl edit deployment apache-deployment
containers:
- name: apache-container
image: httpd:2.4.62 # Updated image
metadata:
annotations:
team: devops
owner: team-a # Added new annotation
Once the edit is saved, a new rollout of pods will take place.
Now let’s perform the same changes through kubectl patch
using the JSON Merge Patch method by running the following command:
kubectl patch deployment apache-deployment --type=merge --patch '{"spec": {"template": {"spec": {"containers": [{"name": "apache-container", "image": "httpd:2.4.62"}]}}}, "metadata": {"annotations": {"owner": "team-a"}}}'
This will also trigger a rollout of new pods.
As you can see, kubectl edit
can be more convenient for making multiple changes simultaneously because it allows you to modify the entire resource interactively without needing to drill into specific paths. kubectl patch
requires greater precision in targeting specific fields, which can make multiple updates more tedious.
It’s important to note that kubectl edit
is more error-prone than kubectl patch
because it does not provide the same level of validation. Whereas kubectl patch
validates the command and ensures that the specified fields exist and are correct before making changes, kubectl edit
relies entirely on the user’s accuracy when editing the resource directly. This lack of validation can lead to unintended errors if the resource is edited incorrectly.
Here are a few best practices to follow when using kubectl patch
:
1. Always validate the resource path
kubectl patch
requires a proper resource path before running the command. You can run the following to grab the correct path of the field you want to update:
kubectl get <resource> <name> -o yaml
2. Choose the right patching strategy
- Strategic Merge Patch: This is the default patching strategy, ideal for updating Pod template fields, adding labels/annotations and making partial updates to lists.
- JSON Merge Patch: Best used to overwrite a single field or entire sections. For example, if you want to replace a pod definition completely with a new definition, it will wipe out the previous definition and place whatever is in the command line. It’s also useful to use this strategy to remove optional fields by setting them to null.
- JSON Patch: Provides fine-grained control through explicit operations. This strategy is best used to add and remove items to a list without overwriting the entire list. Replacing specific fields, and conditional testing before updating and moving fields.
3. Dry run patching commands before running them
Utilize the --dry-run=client
to avoid making any accidental changes through kubectl patch
. You can run the following to validate the changes the command will perform:
kubectl patch deployment apache-deployment --type=json --patch '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "httpd:2.4.62"}]' --dry-run=client -o yaml
4. Validate resource after patching
After running kubectl patch
, it’s highly recommended to check the YAML of your Kubernetes resource to ensure the correct changes were made and other fields did not change. You can run the following to validate:
kubectl get deployment apache-deployment -o yaml
5. Avoid patching immutable fields
There are certain fields in Kubernetes that cannot be edited or modified using kubectl patch
, to modify those fields, you will have to export the existing resource’s YAML file, delete the resource and re-deploy it:
kubectl get deployment apache-deployment -o yaml > deployment.yaml
kubectl delete deployment apache-deployment
kubectl apply -f deployment.yaml
6. Document all changes made with kubectl patch
It’s critical to always document all the changes made by kubectl patch
across the team, especially if the changes are made on production Kubernetes clusters. You can easily lose track of changes made through patching commands, which will cause inconsistencies in future deployments.
You can either update the existing YAML manifest file to always be on par with the existing deployment or update the shared knowledge base.
7. Use kubectl apply for version controlled environments
If your environment is utilizing GitOps principals and CI/CD pipelines, it will be easier to continue making changes on the YAML manifest and use kubectl apply
instead of mixing with kubectl patch.
However, if you need to run kubectl patch
to test and ensure certain changes will work in your existing application then you should make sure to update your existing YAML files with the modifications. kubectl patch
works better as an ad-hoc method.
If you need any assistance with managing your Kubernetes projects, consider Spacelift. It brings with it a GitOps flow, so your Kubernetes Deployments are synced with your Kubernetes Stacks, and pull requests show you a preview of what they’re planning to change.
With Spacelift, you get:
- Policies to control what kind of resources engineers can create, what parameters they can have, how many approvals you need for a run, what kind of task you execute, what happens when a pull request is open, and where to send your notifications
- Stack dependencies to build multi-infrastructure automation workflows with dependencies, having the ability to build a workflow that can combine Terraform with Kubernetes, Ansible, and other infrastructure-as-code (IaC) tools such as OpenTofu, Pulumi, and CloudFormation,
- Self-service infrastructure via Blueprints, or Spacelift’s Kubernetes operator, enabling your developers to do what matters – developing application code while not sacrificing control
- Creature comforts such as contexts (reusable containers for your environment variables, files, and hooks), and the ability to run arbitrary code
- Drift detection and optional remediation
If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.
The kubectl patch
command facilitates precise, ad-hoc updates to Kubernetes resources without redeploying or rewriting YAML manifests. It supports three strategies — Strategic Merge Patch, JSON Merge Patch, and JSON Patch — offering flexibility for targeted changes such as updating images or labels. Although it is useful for real-time updates, it can cause unintended changes when used improperly.
Teams following GitOps should favor kubectl apply
and integrate any kubectl patch
changes back into version-controlled YAMLs to maintain consistency. When used thoughtfully, kubectl patch
provides precision and control for dynamic environments.
Manage Kubernetes Easier and Faster
Spacelift allows you to automate, audit, secure, and continuously deliver your infrastructure. It helps overcome common state management issues and adds several must-have features for infrastructure management.