The Practitioner’s Guide to Scaling Infrastructure as Code

➡️ Download Now

Kubernetes

Kubectl Patch Command & How to Use It With Examples

kubectl patch

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:

  1. Overview of Kubernetes resources
  2. What is the kubectl patch command?
  3. How to use the kubectl patch command with different types of Kubernetes patching strategies
  4. Kubectl patch vs. kubectl edit
  5. Kubectl patch vs. kubectl apply
  6. Tips and best practices for running kubectl patch

Overview of Kubernetes resources

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.

What is the kubectl patch command?

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.

How to use the kubectl patch command with different types of Kubernetes patching strategies

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. 

kubectl patch vs. kubectl apply

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 patch vs. kubectl edit

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.

Tips and best practices for running kubectl patch

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. 

Managing Kubernetes with Spacelift

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.

Key points

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.

Learn more

The Practitioner’s Guide to Scaling Infrastructure as Code

Transform your IaC management to scale

securely, efficiently, and productively

into the future.

ebook global banner
Share your data and download the guide