Sign in
← Kubernetes: basics
6 min · easy · Lesson 5 of 11

Pod in depth: command, args, restartPolicy

How to override how a container starts, and what the cluster does after it finishes.

In the last lesson you started a Pod and kept the container alive with the line command: ["sleep", "3600"]. Now let's look at what that line actually does, how to pick the program a container runs without rebuilding the image, and what Kubernetes does once the container has finished.

Concepts

command and args

Every image has a default program it runs (the Dockerfile's ENTRYPOINT and CMD). The command and args fields on a container override that behavior.

command replaces the default program. args sets its arguments.

spec:
  containers:
    - name: app
      image: busybox:1.36.1
      command: ["sh", "-c"]
      args: ["echo hi; sleep 3600"]

Here the container runs sh -c "echo hi; sleep 3600": it prints hi and sleeps for an hour. sh -c means "run this string as a shell script". Without -c, the shell would decide the string is the name of a script file, and wouldn't find it.

You can put the whole command in command if that's more convenient:

command: ["sh", "-c", "echo hi; sleep 3600"]

Each list item becomes a separate argument. Kubernetes itself never splits a string into words: ["sleep", "3600"] and ["sleep 3600"] are different things. In the second case the cluster will look for a program literally named sleep 3600, with a space in it.

The Docker equivalent: command in a Pod is the container's ENTRYPOINT, args is its CMD.

Pod fieldWhat it overridesWhat it sets
commandENTRYPOINTthe program itself
argsCMDthe program's arguments

What runs for each combination:

busybox has an empty ENTRYPOINT, and CMD equal to ["sh"]. You can see this in the image itself:

docker image inspect busybox:1.36.1 --format '{{json .Config.Entrypoint}} {{json .Config.Cmd}}'
null ["sh"]

That's why even args: ["sleep", "3600"] without command starts sleep 3600 on busybox: there's nowhere to attach arguments to, so args becomes the whole command. On an image with an ENTRYPOINT (nginx, say, or a Go application), args would become arguments to its program instead.

kubectl run can also set the command. Everything after -- goes into args. With the --command flag, it goes into command instead:

kubectl run sleeper --image=busybox:1.36.1 --command -- sleep 3600

Why override the command

A base image often supports more than one scenario. The same busybox can run a web server, do a one-off task, or just stay alive for debugging. command/args pick which one, without rebuilding the image.

That's where the typical sleep trick comes from. Without it the container exits right after the echo. kubelet starts it again, it exits again, and so on: the Pod's phase stays Running, while STATUS shows CrashLoopBackOff.

restartPolicy

restartPolicy is a field in spec that says what to do once a container has finished. It's usually set once for the whole Pod, and it applies to every container in it. It has three values.

restartPolicyExit code 0Exit with an error
Alwaysrestartrestart
OnFailureleave as isrestart
Neverleave as isleave as is
spec:
  restartPolicy: OnFailure
  containers:
    - name: app
      image: busybox:1.36.1
      command: ["sh", "-c", "exit 1"]

This container exits with code 1, and kubelet will keep restarting it again and again. Set exit 0, and after the very first run the Pod moves to phase Succeeded.

A container with sleep 3600 finishes after an hour. With restartPolicy: Always, kubelet starts it again. That's why a web service with Always doesn't go down from a single crash: the container dies, kubelet brings it back up, and the Pod answers requests again.

The choice of policy depends on the job. A long-running service needs Always. A one-off task that should run once and not repeat without an error fits OnFailure or Never (such tasks are usually set up as a Job, a separate lesson).

As of Kubernetes 1.35, restartPolicy can also be set on an individual container, next to image. Then it overrides the Pod's policy for that container. This is the ContainerRestartRules beta feature, enabled by default. In this course, a policy at the Pod level is enough.

You can't change restartPolicy, command, or args on a Pod that already exists. kubectl apply with a new value will return an error:

The Pod "app" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,...

For such an edit to take effect, the Pod gets deleted (kubectl delete pod <name>) and created again from the fixed file.

How this shows up in the cluster

The RESTARTS column in kubectl get pod shows how many times the container has restarted. If the number keeps climbing, the container is crashing and the policy keeps bringing it back. Between attempts kubelet waits, and the pause grows: 10s, 20s, 40s, and on up to 5 minutes. While it's waiting, STATUS shows CrashLoopBackOff.

Here's what Pods with the same image, but different policies and exit codes, showed a minute after starting:

restartPolicyExit codeSTATUSRESTARTSPhase
Always0Completed or CrashLoopBackOff2 and climbingRunning
OnFailure0Completed0Succeeded
OnFailure1Error or CrashLoopBackOff2 and climbingRunning
Never1Error0Failed

Look at the first row. CrashLoopBackOff can happen to a container that ran with no errors at all: with Always, kubelet restarts it anyway.

The exit code of the last run shows up in kubectl describe pod <name>, in the container block, in the Last State and Exit Code lines. What the program managed to print shows up in kubectl logs <name>. Debugging crashes gets its own lessons later in the course.

Common mistakes

Homework

Practice the theory on your own cluster: 2 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-pod-deep     # set up the starting state
./run.sh verify k8s-pod-deep    # check the current step

In short

What you'll need for the tasks

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/pod-deep