Resources

Kubectl Apply vs Create

October 18, 2021

This article explains the differences between kubectl apply and create, and gives examples of when it’s best to use each command in Kubernetes.

Vishnu Chilamakuru
Search & Backend Engineer

kubectl is the Kubernetes command line tool (CLI) that allows you to run commands against Kubernetes clusters. kubectl can be used to deploy applications, inspect and manage cluster resources, and view logs.

In a platform as large as Kubernetes, you will find commands that are similar but serve slightly different purposes. kubectl <terminal inline>apply<terminal inline> and <terminal inline>create<terminal inline> commands are two different approaches for creating Kubernetes resources. In this article, we will explore what these commands do, what the key differences between them are, and when to use each.

Imperative and Declarative

Before we discuss <terminal inline>apply<terminal inline> and <terminal inline>create<terminal inline>, it’s important to understand the difference between the imperative and declarative approaches.

In the imperative approach, we need to specify what and how to perform a task.
If the task is preparing a sandwich, for example, you would explain the items needed as well as the step-by-step instructions.

In the declarative approach, we need to specify what to do but not how to do it.
In this case, you would only explain the items needed for the sandwich, without the instructions.

Resource Creation

How do the imperative and declarative approaches fit into a Kubernetes context? <terminal inline>kubectl apply<terminal inline> and <terminal inline>kubectl create<terminal inline> are two different approaches in creating resources or updating resource configurations in the Kubernetes cluster environment.

Both these commands accept configuration changes either from the file or stdin, and both accept JSON and YAML file formats.

kubectl create

<terminal inline>kubectl create<terminal inline> is for imperative management. In this approach, you tell the Kubernetes API what you want to create, replace, or delete.

In simpler terms, <terminal inline>create<terminal inline> produces a new object (previously nonexistent or deleted). If a resource already exists, it will raise an error.

kubectl apply

<terminal inline>kubectl apply<terminal inline> is part of the declarative management approach in which changes that you may have applied to a live object (i.e., through scale) are “maintained” even if you apply other changes to the object.

<terminal inline>apply<terminal inline> makes incremental changes to an existing object by defining what we need. If a resource already exists, it will not raise an error.

apply vs create: Attributes

kubectl apply kubectl create
1. It directly updates in the current live source with only the attributes that are given in the file. It creates resources from the file provided. It shows an error if the resource has already been created.
2. The file used in apply can be an incomplete spec. The file used in create should be complete.
3. apply works only on some properties of the resources. create works on every property of the resources..
4. You can apply a file that changes only an annotation without specifying any other properties of the resource. If you use the same file with a replace command, the command will fail due to the missing information.

How They Work

For this example, let’s use the YAML file below to create a Kubernetes pod.


yaml
→ cat sample-pod.yml

---
apiVersion: v1
kind: Pod
metadata:
 name: kubectl-create-vs-apply-demo
 labels:
  app: front-end
  rel: dev
spec:
 containers:
 - name: httpd
  image: docker.io/httpd
  imagePullPolicy: IfNotPresent
  ports:
   - containerPort: 80

Now create the pod using the imperative approach with <terminal inline>kubectl create<terminal inline>:


shell

→ kubectl create -f sample-pod.yml
pod/kubectl-create-vs-apply-demo created

Let’s verify the pod status along with labels:

shell


→ kubectl get pods --show-labels

NAME READY STATUS RESTARTS AGE LABELS
kubectl-create-vs-apply-demo 1/1 Running 0 98s app=front-end,rel=dev

Now we’ll edit the YAML file and add an extra label to it (environment: local):


yaml
→ cat sample-pod.yml

---
apiVersion: v1
kind: Pod
metadata:
 name: kubectl-create-vs-apply-demo
 labels:
  app: front-end
  rel: dev
  environment: local
spec:
 containers:
 - name: httpd
  image: docker.io/httpd
  imagePullPolicy: IfNotPresent
  ports:
   - containerPort: 80

Once again, use the imperative approach to apply the changes:


shell

→ kubectl create -f sample-pod.yml
Error from server (AlreadyExists): error when creating "sample-pod.yml": pods "kubectl-create-vs-apply-demo" already exists

Note the error message saying the resource <terminal inline>kubectl-create-vs-apply-demo<terminal inline> already exists.

Now we’ll do the same operation using the declarative approach with <terminal inline>kubectl apply<terminal inline>:


shell

→ kubectl apply -f sample-pod.yml
pod/kubectl-create-vs-apply-demo configured

This time, the resource was configured. Let’s verify the changes we made.

shell


→ kubectl get pods --show-labels

NAME READY STATUS RESTARTS AGE LABELS
kubectl-create-vs-apply-demo 1/1 Running 0 8m22s app=front-end, environment=local,rel=dev

You can see that the new label <terminal inline>environment: local<terminal inline> has been applied to the pod.

CI/CD Issues

The differences between these commands can impact your CI/CD pipeline. As we saw above, <terminal inline>kubectl create<terminal inline> can only be used if there are no existing resources and you want to create new resources.

If the resource is already created and running, <terminal inline>create<terminal inline> will raise an error, thereby halting your CI/CD. To avoid that, use the <terminal inline>kubectl get<terminal inline> command to check if there is already an existing resource; then use <terminal inline>kubectl apply<terminal inline> to update to the latest configuration.

Shell Scripts

There will be many scenarios where you will want to execute shell scripts after the launch of the resource. You can provide shell scripts as part of your configuration file, which can be triggered just after the resource is created. These commands, once configured, cannot be updated when the resource is already running.

Be aware that these commands can be executed only via <terminal inline>kubectl create<terminal inline>. kubectl won’t allow you to update these commands via <terminal inline>kubectl apply<terminal inline> if the container is already running. We’ll use a simple example to demonstrate.

We will create a pod with a simple <terminal inline>echo<terminal inline> command.


yaml

---
apiVersion: v1
kind: pod
metadata:
 name: kubectl-create-vs-apply-demo
 labels:
  app: front-end
  rel: dev
  environment: local
spec:
 containers:
 - name: httpd
  image: docker.io/httpd
  imagePullPolicy: IfNotPresent
  command:
  - 'bash'
  - '-c'
  - 'echo "hello world"'
  ports:
   - containerPort: 80

Let’s create the pod using the above configuration.


shell

→ kubectl apply -f sample-pod.yml
pod/kubectl-create-vs-apply-demo created

Now update the <terminal inline>echo<terminal inline> command and apply the changes to the pod.


yaml
  
---
apiVersion: v1
kind: Pod
metadata:
 name: kubectl-create-vs-apply-demo
 labels:
  app: front-end
  rel: dev
  environment: local
spec:
 containers:
 - name: httpd
  image: docker.io/httpd
  imagePullPolicy: IfNotPresent
  command:
  - 'bash'
  - '-c'
  - 'echo "hello world with example"'
  ports:
   - containerPort: 80

Apply the changes to the pod using the above configuration.


shell

→ kubectl apply -f sample-pod.yml
the pod "kubectl-create-vs-apply-demo" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)
 core.PodSpec{
  volumes: {{Name: "kube-api-access-4lg9p", VolumeSource: {Projected: &{Sources: {{ServiceAccountToken: &{ExpirationSeconds: 3607, Path: "token" }}, {ConfigMap: &{LocalObjectReference: {Name: "kube-root-ca.crt"}, Items: {{Key: "ca.crt", Path: "ca.crt"}}}}, {DownwardAPI: &{Items: {{Path: "namespace", FieldRef: &{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}}}, DefaultMode: &420}}}},
   InitContainers: nil,
   Containers: []core.Container{
    {
     Name: "httpd",
     Image: "docker.io/httpd",
     Command: []string{
      "bash",
      "-c",
      - `echo "hello world with example" ,
      + `echo "hello world" ,
      },
      Args: nil,
      WorkingDir: "",
      ... // 17 identical fields
    },
   },
   EphemeralContainers: nil,
   RestartPolicy: "Always",
   ... // 25 identical fields
 }

So <terminal inline>kubectl apply<terminal inline> failed with the stated reason that pod updates cannot change fields other than container specs.

Which Should You Use?

Choosing to use either <terminal inline>create<terminal inline> or <terminal inline>apply<terminal inline> depends on your particular use case.

If you want to add a version control to your Kubernetes object, then it’s better to use <terminal inline>kubectl apply<terminal inline>, which helps determine the accuracy of data in Kubernetes objects.

If you want to create a resource for troubleshooting, learning, or interactive experimentation, go with <terminal inline>kubectl create<terminal inline>.

Conclusion

Now you know what the difference is between imperative and declarative approaches and how they fit into the context of Kubernetes. You should also have a better sense of when to use kubectl <terminal inline>apply<terminal inline> and <terminal inline>create<terminal inline> and in what context. This will help you make more efficient use of your time and resources in Kubernetes and keep your CI/CD workflow at peak performance.

Article by

Vishnu Chilamakuru

Search & Backend Engineer

Technology and startup enthusiast who works as a Search & Backend Engineer at Class Central, after having held a number of Principal Engineering positions at other companies. Vishnu writes about system design, databases, microservices, backend development, developer tools.

Read More