Sign in
← Kubernetes: basics
8 min · medium · Lesson 7 of 11

Imperative kubectl: run, create, expose, scale

The set of commands that create resources without YAML, and when each one fits.

A set of commands that create resources without YAML, and when each one fits. You'll need everything from the lesson on the first Pod and on dry-run.

Imperative and declarative

Kubernetes has two styles of working with resources.

Imperative: you tell kubectl what to do right now. kubectl create deployment app --image=nginx:1.27.3-alpine is the command "create a Deployment with these parameters". If that Deployment already exists, the command fails:

error: failed to create deployment: deployments.apps "app" already exists

kubectl builds the manifest itself and sends it to the API server.

Declarative: you write the desired state in YAML and call kubectl apply -f. If the resource exists, kubectl compares it with the manifest and updates it. If not, it creates it.

CommandResource doesn't existResource already exists
kubectl createcreatesfails with already exists
kubectl apply -fcreatescompares and updates

Production works declaratively: YAML lives in git, and every change is visible through a merge request. For a quick hypothesis check or one-off debugging, imperative is faster.

Map: what to use when

There are a lot of commands below. To keep track, remember which command fits which task:

Next, each command on its own.

kubectl run

Creates a single Pod. The most compact command in kubectl.

kubectl run temp --image=busybox:1.36.1 --restart=Never -- sh -c 'sleep 3600'

How to parse it: Pod name, then flags, then after -- the command for the container.

k run temp --image=busybox:1.36.1 -- sleep 60
  │   │    │                         └ command
  │   │    └ flags
  │   └ Pod name
  └ subcommand

--restart sets restartPolicy. Values: Always (the default), OnFailure, Never. Older kubectl created a Deployment with --restart=Always and a single Pod with Never. That logic was removed in kubectl 1.18. Now run always creates a Pod, and the --restart value just goes into its spec.

Useful run flags:

kubectl create

Creates resources above the Pod level: Deployment, Service, ConfigMap, Secret, Job, CronJob, Namespace, ServiceAccount.

kubectl create deployment myapp --image=nginx:1.27.3-alpine \
  --replicas=3 --port=80

kubectl create configmap app-cfg --from-literal=MODE=prod

kubectl create secret generic db --from-literal=PASS=lab-only-password

kubectl create job hello --image=busybox:1.36.1 -- echo done

kubectl create cronjob clean --image=busybox:1.36.1 \
  --schedule='*/5 * * * *' -- sh -c 'echo tick'

Unlike kubectl run, you name the resource type yourself: deployment, configmap, job. Short names work too: deploy, cm.

kubectl create deployment fills in two things on its own, which other commands later ask about:

kubectl expose

Creates a Service on top of an existing Pod, Deployment, ReplicaSet, or another Service. The most common case: on top of a Deployment.

kubectl expose deployment myapp --port=8080 --target-port=80 --name=myapp-svc

--port is the Service's own port, the one clients hit. --target-port is the container port the Service forwards the request to. If --target-port isn't set, it equals --port.

expose takes the selector from the Deployment (its spec.selector), or from the Pod's labels for a Pod. The default type is ClusterIP, change it with --type=NodePort or --type=LoadBalancer. The Service name matches the resource's name by default, the --name flag sets a different one.

Check the result by the addresses the Service picked up:

kubectl get endpointslice -l kubernetes.io/service-name=myapp-svc
NAME              ADDRESSTYPE   PORTS   ENDPOINTS                          AGE
myapp-svc-m4w9v   IPv4          80      10.244.0.8,10.244.0.9,10.244.0.7   7s

The older command kubectl get endpoints myapp-svc shows the same thing, but the v1 Endpoints object was deprecated as of Kubernetes 1.33, and kubectl prints a warning:

Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice

kubectl scale

Changes spec.replicas on a Deployment, ReplicaSet, or StatefulSet.

kubectl scale deployment myapp --replicas=5

Under the hood it's a single PATCH request to the Deployment's scale subresource, the command doesn't touch the object's other fields. You can run it as many times as you want.

--dry-run=client -o yaml

You can turn any imperative command into a YAML generator by adding two flags:

kubectl create deployment myapp --image=nginx:1.27.3-alpine \
  --replicas=3 --port=80 --dry-run=client -o yaml > deploy.yaml

--dry-run=client means "don't send it to the API, just build the manifest locally." -o yaml means "print it as YAML." The result is a ready skeleton you can edit by hand: add probes, resources, volumes, securityContext. dry-run is covered in detail in the previous lesson.

The basic habit: you don't write YAML from scratch, you generate it and edit it by hand.

# Pod
kubectl run pod1 --image=nginx:1.27.3-alpine --dry-run=client -o yaml

# Deployment
kubectl create deployment d1 --image=nginx:1.27.3-alpine --dry-run=client -o yaml

# ConfigMap
kubectl create cm c1 --from-literal=k=v --dry-run=client -o yaml

# Job
kubectl create job j1 --image=busybox:1.36.1 --dry-run=client -o yaml -- sh -c 'echo hi'

# ClusterIP Service on top of an already created Deployment myapp
kubectl expose deployment myapp --port=80 --dry-run=client -o yaml

expose has a quirk. It reads the selector from the live object, so the Deployment must already exist in the cluster. For a Deployment that doesn't exist, even with --dry-run=client you get an error:

Error from server (NotFound): deployments.apps "d1" not found

kubectl set

Changes specific fields on an already created resource. Faster than edit.

# Change the nginx container's image
kubectl set image deployment/myapp nginx=nginx:1.27.4-alpine

# Change an env var
kubectl set env deployment/myapp DEBUG=true

# Change resources
kubectl set resources deployment/myapp --limits=cpu=200m,memory=256Mi

In set image, the name to the left of = is the container name, not the Deployment's. Get it wrong and kubectl answers:

error: unable to find container named "app"

The container name is visible in kubectl describe deployment myapp, the line under Containers:.

Each of these commands changes the Pod template, so the Deployment starts a rollout: it creates new Pods and removes the old ones. The rollout's progress shows in kubectl rollout status deployment/myapp.

kubectl edit

Opens the resource's current YAML in the editor from the KUBE_EDITOR or EDITOR variable. If neither is set, kubectl uses vi, and notepad on Windows. Save it, and kubectl sends the change. Handy for one-off edits, but production doesn't work this way: the change isn't in git, and the next apply from the repository will overwrite it.

kubectl edit deployment myapp

Common mistakes

Running kubectl create again on an existing resource. It doesn't update the object, it fails with already exists. Delete the object and recreate it, or fix it with set, scale.

Mixed-up ports in kubectl expose. The client hits --port, the app listens on --target-port. If --target-port doesn't match the port the container listens on, a request through the Service gets Connection refused. kubectl describe service helps you compare ports: the Port, TargetPort, and Endpoints lines.

A container command without -- in kubectl run. Everything after -- goes to the container. Without a long-running command, a Pod on busybox finishes right away and goes to Completed.

An image without a tag in --image. Then you get whatever's currently under the default tag in the registry, and tomorrow that might be a different version. Write the tag explicitly, and after set image check kubectl get pods: a typo in the tag shows up as ErrImagePull or ImagePullBackOff.

What you'll need for the tasks

Homework

Practice the theory on your own cluster: 4 steps with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.

./run.sh start k8s-imperative-commands     # set up the starting state
./run.sh verify k8s-imperative-commands    # check the current step

In short

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson k8s-basics-07-imperative-commands