3.2. Controllers
When to Look at This
- When checking how many instances of an application are up
- When confirming that a new version was deployed properly
- When increasing or decreasing the pod count
- When a problem appears after a deployment and you have to go back
What a Controller Is
A pod by itself is not recreated. If you create a pod directly, the application disappears the moment that pod terminates. The same is true if the node goes down.
Controllers solve this. You define the "desired state", and the controller keeps watching the cluster and brings the actual state in line with it.
For example, if you define "three replicas", it behaves like this.
| What actually happened | What the controller does |
|---|---|
| One pod terminated (two remain) | Creates one new pod to make three |
| A node went down and two pods disappeared | Starts two new ones on the remaining nodes |
| Someone deleted a pod by hand | Creates it again |
| The replica count was changed to 5 | Creates two more |
Nobody has to watch it. This is one of the biggest reasons for using Kubernetes.
What "Declarative" Means
The word "declarative" comes up when Kubernetes is explained. Comparing it with imperative makes it easier.
| Style | The instruction | When a problem occurs |
|---|---|---|
| Imperative | "Run this program" | Nothing happens if it terminates |
| Declarative | "Three of this program must be up" | The system restores it if one terminates |
Controllers are the mechanism that realizes the declarative style. You write only the outcome, and the controller takes charge of reaching it.
Changing the replica count or editing the image in the Console is editing the "desired state". It is not touching the actual pods.
What a Deployment Is
It is the controller for managing applications that do not store state. Use it for web servers, API servers, and anything else where the result is the same no matter which pod receives the request.
A Deployment does three things.
| What it does | Description |
|---|---|
| Maintains the count | Keeps the defined replica count |
| Replaces without downtime | Swaps pods one at a time when deploying a new version, so the service is not interrupted |
| Rolls back | Keeps a record of previous versions so you can return if a problem appears |
"Does not store state" means there is no data that has to remain inside the pod. Data lives in a database or separate storage, and the pod must not care when it terminates and is recreated.
If each pod needs its own data, use a StatefulSet rather than a Deployment (see below).
How a Deployment Manages Pods
A Deployment does not create pods directly. There is a ReplicaSet in between.
Deploying a new version creates one more ReplicaSet. The old ReplicaSet remains at zero replicas as history. Rolling back means raising the replicas of the old ReplicaSet again.
Seeing several ReplicaSets sharing the same name prefix in the list is the deployment history so far.
The Deployment List
Go to Workloads > Deployments.

| Column | Description | How to read it |
|---|---|---|
| Name | The Deployment name | |
| Namespace | The namespace it belongs to | |
| Pods | Ready pods / desired pods | If the two numbers differ, a deployment is in progress or something is wrong |
| Replicas | The configured replica count | |
| Age | Time elapsed since creation |
If 2/3 persists for more than a few minutes, find the pod that is not ready in the pod list and check its events
(see 3.1).
Deployment Detail
Selecting the name opens the detail screen.

| Section | What it tells you |
|---|---|
| Metadata | Name, namespace, labels, annotations |
| Strategy | How it switches to a new version |
| Selectors | Which labeled pods it considers its own |
| Containers | Image, tag, environment variables, requested and limited resources |
| Conditions | Deployment progress |
| Related resources | The ReplicaSets and pods that were created |
Check the container image tag. This is where you see whether the version you deployed actually took effect.
The selector cannot be changed after creation. It is how the Deployment finds its own pods, so changing it breaks the link with the existing pods.
Read the deployment state from the conditions.
| Condition | Healthy value | If it is otherwise |
|---|---|---|
| Available | True | Not enough pods are ready |
| Progressing | True (NewReplicaSetAvailable) | The deployment has stalled. Check the reason |
If a deployment does not finish within a set time (10 minutes by default), it turns to
ProgressDeadlineExceeded. That means new pods cannot start, so look at the pod events.
If new pods are Running but the deployment does not finish, check the readiness probe. A rolling update
replaces the next pod only when the new pod becomes ready. If the probe keeps failing, it stalls at the first pod
and makes no progress (see 3.1).
Deployment Strategy Settings
| Strategy | Behavior | When to use it |
|---|---|---|
| RollingUpdate | Starts new pods one at a time and takes old pods down one at a time | The default. The service is not interrupted |
| Recreate | Takes all old pods down, then starts the new ones | When two versions must not be up at the same time |
Recreate stops the service briefly. Use it only when a database schema change means the old and new versions
cannot coexist.
RollingUpdate has two tuning values.
| Value | Meaning | Default |
|---|---|---|
| maxSurge | How many more than the desired count may be up | 25% |
| maxUnavailable | How many may be missing at once | 25% |
Setting maxUnavailable: 0 keeps the healthy pod count intact during a deployment. In exchange, more resources are
needed briefly.
Write the settings like this.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-app
spec:
replicas: 4
revisionHistoryLimit: 10 # how many revisions can be rolled back to
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # a count or a ratio ("25%")
maxUnavailable: 0 # keeps 4 up even during the deployment
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app # must match the selector
annotations:
kubernetes.io/change-cause: "1.2.0 - fix login error"
spec:
containers:
- name: app
image: registry.example.com/my-app:1.2.0
| Field | Description |
|---|---|
maxSurge | How many more than the desired count may be started. 0 uses no extra resources but makes the deployment slower |
maxUnavailable | How many may be missing at once. 0 means the serving count never drops |
revisionHistoryLimit | How many revisions to keep. Beyond this number the oldest are deleted and cannot be rolled back to |
change-cause annotation | Appears in the Change Cause column of the deployment history |
maxSurge and maxUnavailable cannot both be 0. Nothing could be started and nothing taken down, so the
deployment would not proceed. It is rejected on apply.
Include change-cause. Without it, the change cause in the deployment history stays <none>, and you have to
choose a revision to roll back to from the image tag and time alone.
Recreate has no tuning values.
strategy:
type: Recreate
Deployment styles that also involve Services, such as blue-green and canary, are covered in 4.8.
The strategy in this section is a setting contained within a single Deployment.
What a StatefulSet Is
It is the controller for managing applications where each pod needs its own identity and storage.
The pods of a Deployment are not distinguishable from one another. A random string is appended to the name, and the name changes when a pod is recreated. Storage is shared or absent.
A StatefulSet is different.
| Characteristic | Deployment | StatefulSet |
|---|---|---|
| Pod name | Random (web-7d4f-x8k2) | Ordinal (db-0, db-1, db-2) |
| Creation order | All at once | From 0, in order |
| Deletion order | All at once | From the last ordinal, in reverse |
| Storage | Shared or absent | One fixed volume per pod |
| Network name | None | A fixed name per pod |
The key point is "the same thing even after being recreated". If db-0 terminates and comes back, its name is
still db-0 and it reconnects to the storage it was using before. The data is preserved.
When to Use a StatefulSet
| Situation | Reason |
|---|---|
| Databases (MySQL, PostgreSQL) | Each instance has its own data files |
| Message queues (Kafka) | Partitions are assigned to particular instances |
| Distributed stores (Elasticsearch) | Each node holds different data |
| When cluster members must find each other by name | Fixed network names are needed |
Why creating in order matters. A database cluster usually has the first instance start and initialize, after which the rest join it. Starting all at once makes them fail to find each other.
Deleting in order matters too. Removing from the last ordinal keeps the remaining members in quorum.
View them under Workloads > Stateful Sets. The screen layout is the same as for Deployments.
What a DaemonSet Is
It is the controller that runs one pod on every node. You do not set a replica count; it matches the node count automatically.
| Situation | Behavior |
|---|---|
| A new node is added to the cluster | A pod is created on that node automatically |
| A node is removed from the cluster | That node's pod disappears |
Use it for things that must exist once per node.
| Use | Example |
|---|---|
| Log collection | Gathers and ships the container logs of each node |
| Metric collection | Measures and reports node CPU and memory |
| Networking | Handles communication between nodes |
| Storage | Provides the node's disk to pods |
View them under Workloads > Daemon Sets. The desired count is usually the same as the node count.
A count lower than the node count means it could not be scheduled on some nodes. Nodes with taints are excluded unless there is a toleration (see 2.1), so management nodes being absent can be normal.
What a ReplicaSet Is
It is the controller that maintains a set number of pods. It only maintains the count; it has no version management or zero-downtime replacement.
You do not create them directly. A Deployment creates and uses them internally. What appears in the list is deployment history, so do not delete them.
View them under Workloads > Replica Sets.
| State | Meaning |
|---|---|
| Replicas not 0 | The version currently in use |
| Replicas 0 | A previous version. Used for rollback |
Which Controller to Choose
| Question | Yes | No |
|---|---|---|
| Does each pod have to store different data? | StatefulSet | Next question |
| Does there have to be one on every node? | DaemonSet | Next question |
| Is it work that runs once and finishes? | Job or CronJob (see 3.3) | Deployment |
A Deployment is enough for most applications.
Adjusting the Replica Count
Select the scale button at the top right of the detail screen.
- Select the scale button.
- Enter the replica count you want.
- Saving applies it immediately.
To adjust automatically with load, use autoscaling (see 3.5).
Changing the replica count by hand on a resource with autoscaling configured soon reverts to the automatic value.
Restarting
Selecting the Restart button at the top right of the detail screen starts the pods anew without changing any settings.
Use it to apply values after editing a ConfigMap or Secret. Values injected as environment variables change only when the pod is started again (see 3.4).
A restart proceeds as a rolling update, so the service is not interrupted. The result is the same as deleting pods one at a time, but the order is controlled.
Here is how it works. The Console adds a "restarted at" annotation to the pod template. Because the template changed, Kubernetes treats it as a new version deployment and starts a rolling update. The image and settings stay the same.
Viewing Deployment History
Selecting the clock icon (Deployment History) at the top right of the detail screen shows the record of past deployments.
Three resource types are supported.
| Resource | View history | Roll back |
|---|---|---|
| Deployment | Yes | Yes |
| DaemonSet | Yes | Yes |
| StatefulSet | Yes | No (history only) |
The history table shows the following.
| Column | Description |
|---|---|
| Revision | The deployment ordinal. Higher is more recent |
| Name | The name of the ReplicaSet holding that revision |
| Image | The container image and tag deployed at that time |
| Change Cause | The description left at deployment time. <none> if absent |
| Creation time | When that revision was created |
| Status | The revision currently in use is marked Current |
The image tag tells you which version it is right away. If you pin the tag to latest, every revision shows the
same value and they cannot be told apart, so use different tags when building (see 4.2).
Change Cause is filled in only if you add the kubernetes.io/change-cause annotation when deploying. Without it,
it stays <none>. Recording why you deployed makes it easier to pick a revision to roll back to later.
How to Roll Back
Return to a previous state when a problem appears after deploying a new version.
- Open the detail screen of the resource to roll back (a Deployment or DaemonSet).
- Select the clock icon at the top right to open the Deployment History.

- Find the revision to return to. Identify it by image tag and creation time.
- Select the revert icon in the Actions column on that row.
- A rolling update starts. The history updates shortly after.
The revision currently in use has no revert icon. In the figure above, revision 183 carries the Current marker and the Active state, and its Actions cell is empty. It is already that state, so there is nothing to revert to. The remaining rows are Inactive and each has a revert icon.
Choose the revision to return to by image tag and creation time. If the image tags are all the same, as in the figure above, the revision alone does not tell you what changed. In that case use the creation time to find "the last deployment before the problem appeared". If you recorded change causes, that value is the surest clue.
What a Rollback Reverts
The entire pod template returns to the state of that revision.
| Item | Does it revert |
|---|---|
| Container image and tag | Yes |
| Environment variables | Yes |
| Resource requests and limits | Yes |
| The names of attached volumes, ConfigMaps, and Secrets | Yes |
| Replica count | No. The current value is kept |
| The contents of ConfigMaps and Secrets | No. Only the references revert |
| Database data | No |
Leaving the replica count alone is intentional. A rollback reverts "which version to run", not "how many to run".
ConfigMap contents do not revert. If editing a setting value was the problem, a rollback alone does not solve it. The ConfigMap has to be reverted as well (see 3.4).
What to Check After a Rollback
| Check | Where |
|---|---|
| Whether new pods started and became ready | Workloads > Pods (see 3.1) |
| Whether the image tag is the intended one | Containers on the Deployment detail |
| Whether the revision count increased in the history | Deployment History |
Revision numbers move forward even on a rollback. Reverting from revision 5 to 3 creates revision 6 with the same content as 3, because the revert is itself recorded as a deployment.
When a Rollback Does Not Work
| Symptom | Cause | Remedy |
|---|---|---|
| The revision to revert to is not in the list | It was deleted beyond the retention count | See "History Retention Count" below |
| The rollback ran but pods will not start | The image used at that time was deleted from the registry | Upload the image again or rebuild |
| The rollback ran but the symptom persists | The cause is in configuration or data, not the application | Check the ConfigMap, Secret, and external integrations |
| There is no rollback button on the StatefulSet | It is not supported | See below |
StatefulSets offer history only and cannot be rolled back. Each pod has storage attached, so reverting the version leaves the data as it is, and the older version may not be able to read data in the newer format. If you must revert, edit the image tag back to the previous value directly and confirm data compatibility first.
History Retention Count
As many revisions are kept as the Revision History Limit value on the Deployment detail. The default is 10.
Beyond this number, the oldest revisions are deleted and cannot be rolled back to. Raise the value for applications that deploy frequently.
Do not delete ReplicaSets with 0 replicas from the list. They are the history.