Practical guide to Kubernetes probes
Probes provide an essential configuration element to design self-healing and robust deployments.
Written by
Technical Writer at Civo
Written by
Technical Writer at Civo
A container that is still running is not the same as a container that is still working. A process can be alive in every sense the kernel cares about while the application inside it has wedged on a deadlock, lost its database connection, or filled its heap. Kubernetes, left to its own devices, sees a running process and assumes all is well, so traffic keeps flowing to a pod that can no longer serve it.
In this guide, we'll walk through the three Kubernetes probes, the methods they use to check health, and how to tune them so your deployments heal themselves instead of quietly failing.
Prerequisites
To follow along, you'll need:
- A Civo account
- A Kubernetes cluster created on Civo
- The latest
kubectlutility to interact with the cluster - The
KUBECONFIGfile pointingkubectlat your cluster, downloadable from the cluster page on your Civo dashboard
If you do not have a cluster yet, you can create one from the Civo dashboard and download the kubeconfig in a couple of minutes.
Why probes matter
Kubernetes has become the de-facto standard for deploying cloud-native applications, driven in large part by the shift to microservices. That style of system, with lightweight, loosely coupled, autonomous services, suits distributed applications well. The trade-off is that managing those applications at scale is hard, especially when many components depend on one another and you need the system to stay correct even during partial failures.
By default, the only health signal Kubernetes has is whether the container's main process is running. That is a weak signal. An application can return errors on every request, refuse new connections, or hang indefinitely while its process stays up. Probes give Kubernetes an application-specific health signal so it can act: restart a wedged container, stop routing traffic to one that is not ready, or wait patiently for a slow boot to finish.
Before we add any probes, let's set up a namespace and a plain deployment to work against.
kubectl create namespace probe-demokubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80EOF
This is a healthy baseline with no probes. Everything below builds on it.
The three probe types
Kubernetes gives us three probes, each answering a different question:
- Liveness probe: Constantly checks whether the container is healthy and functional. If it detects an issue, by default it restarts the container.
- Readiness probe: Checks whether the container is ready to accept incoming requests. If it is, requests are sent to the container; if not, the pod is removed from the Service's endpoints.
- Startup probe: Determines whether a container has started. While it runs, the liveness and readiness probes are held off so a slow boot does not trigger a premature restart.
Which probe when
If you remember nothing else, remember this:
- Use a liveness probe to restart a container that has wedged but is still running. If it fails, the kubelet kills and restarts the container.
- Use a readiness probe to decide whether a container should receive traffic. If it fails, the pod is pulled from Service endpoints but is not restarted.
- Use a startup probe for slow-starting apps. While it runs, liveness and readiness are held off, so a long boot does not trigger premature restarts.
Probe methods
Every probe type checks health using one of four methods. The first three have been around since the early days of Kubernetes; the fourth, gRPC, is the modern addition we'll cover at the end of this section.
Exec
An exec probe runs a command inside the container. If the command exits with status 0, the probe succeeds; any non-zero exit is a failure. Here we check that nginx's default index page is present on disk.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:exec:command:- ls- /usr/share/nginx/html/index.htmlEOF
The file exists in the stock image, so ls exits 0 and the probe passes.
TCP
A TCP probe succeeds if the kubelet can open a TCP connection to the given port. Note that the manifest below points the probe at port 8080, while nginx listens on 80. That mismatch is deliberate: the connection will fail, and you can watch the kubelet restart the container as a result.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:tcpSocket:port: 8080EOF
Point the probe at port 80 instead and it will pass.
HTTP
An HTTP probe sends a GET request to a path and port. Any response code from 200 to 399 counts as success; anything else is a failure. Let's start by asking for a path that does not exist so we can see a failure.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:httpGet:path: /non-existing-endpointport: 80EOF
That path returns a 404, so the probe fails and the container restarts. Point it at / and it succeeds.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:httpGet:path: /port: 80EOF
Tip: name your ports
Instead of repeating a port number across the manifest, name the container port and reference it by name in the probe. This keeps the probe in sync if the port ever changes.
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata:name: named-port-probenamespace: probe-demospec:containers:- name: appimage: registry.k8s.io/e2e-test-images/agnhost:2.40args: ["liveness"]ports:- name: healthcontainerPort: 8080livenessProbe:httpGet:path: /healthzport: healthinitialDelaySeconds: 5periodSeconds: 5EOF
The probe references the named port and resolves with no error:
Liveness: http-get http://:health/healthz delay=5s timeout=1s period=5s
gRPC
If your service speaks gRPC, you don't need to bolt on an HTTP endpoint or ship a separate probe binary. Since Kubernetes 1.27 you can point a probe straight at the gRPC health checking endpoint with the grpc field. This method is generally available on every supported cluster and needs no feature gate.
The example below uses the maintained agnhost grpc-health-checking test image, which implements the gRPC health checking protocol, so it runs with no extra setup.
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata:name: grpc-livenessnamespace: probe-demospec:containers:- name: agnhostimage: registry.k8s.io/e2e-test-images/agnhost:2.40args: ["grpc-health-checking"]ports:- containerPort: 5000- containerPort: 8080livenessProbe:grpc:port: 5000initialDelaySeconds: 5periodSeconds: 10EOF
The grpc block takes an optional service field. Leave it unset to check the server's overall health, or set it to a specific registered service name. With the probe passing, the pod stays healthy with no restarts:
NAME READY STATUS RESTARTS AGEgrpc-liveness 1/1 Running 0 2m10s
The gRPC liveness probe: pod Running with 0 restarts, no feature gate needed on current Kubernetes.
Seeing liveness restart a container
A liveness probe earns its keep when a container that was healthy goes bad. Let's force that. The deployment below starts nginx, then immediately deletes its index page in a postStart hook. The HTTP liveness probe against / will start failing and the kubelet will restart the container.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:httpGet:path: /port: 80lifecycle:postStart:exec:command: ["/bin/bash", "-c", "rm -f /usr/share/nginx/html/index.html"]EOF
Watch the restart count climb:
kubectl get pods -n probe-demo -w
The liveness probe is not a cure for every problem. It helps when a restart can actually clear the fault, such as an intermittent hang or a corrupted in-memory state. If the underlying issue is permanent, restarting just produces a crash loop, so reach for liveness when a fresh start has a real chance of fixing things.
Adding a readiness probe
Restarting a broken container is good, but while it is broken we should also stop sending it traffic. That is the readiness probe's job. The deployment below keeps the same self-sabotaging postStart hook and adds a readiness probe alongside the liveness probe.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:httpGet:path: /port: 80lifecycle:postStart:exec:command: ["/bin/bash", "-c", "rm -f /usr/share/nginx/html/index.html"]readinessProbe:httpGet:path: /port: 80EOF
When the readiness probe fails, the pod is removed from the Service's endpoints, so clients are not routed to it. The container is not restarted by readiness alone; that is the liveness probe's responsibility. Combining the two gives you the robust pattern most production deployments want: unhealthy pods stop receiving traffic and unhealthy containers get restarted.
Adding a startup probe
Some applications are slow to boot. If a liveness probe starts checking before the app has finished starting, it can kill the container during a perfectly normal, if lengthy, startup. The startup probe solves this. While it runs, both the liveness and readiness probes are disabled, and only once it succeeds do the other two take over.
kubectl apply -f - <<'EOF'apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: probe-demospec:selector:matchLabels:app: nginxreplicas: 2template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80livenessProbe:httpGet:path: /port: 80lifecycle:postStart:exec:command: ["/bin/bash", "-c", "rm -f /usr/share/nginx/html/index.html"]readinessProbe:httpGet:path: /port: 80startupProbe:httpGet:path: /port: 80EOF
This is the key point that trips people up: when a startup probe is configured, it disables the other two probes until it succeeds. Give it a generous failureThreshold and periodSeconds so even a slow application has time to come up.
Advanced probe configuration
Every probe shares the same five tunables. Sensible defaults are built in, but tuning them is what makes a probe fit your application instead of fighting it.
A common combination is a longer initialDelaySeconds to cover startup, a periodSeconds short enough to catch failures quickly, and a failureThreshold high enough to ride out a brief blip without overreacting.
Failing fast with a probe-level grace period
By default, when a liveness probe fails, the container gets the pod's full terminationGracePeriodSeconds to shut down. For a process that is already hung, that wait is wasted. You can set a probe-level terminationGracePeriodSeconds so a liveness failure overrides the pod value and restarts faster. This field is valid on liveness and startup probes only; the API server rejects it on readiness probes.
kubectl apply -f - <<'EOF'apiVersion: v1kind: Podmetadata:name: liveness-fast-restartnamespace: probe-demospec:terminationGracePeriodSeconds: 600containers:- name: appimage: registry.k8s.io/e2e-test-images/agnhost:2.40args: ["liveness"]livenessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 5periodSeconds: 5failureThreshold: 1# a liveness failure overrides the pod's 600s grace periodterminationGracePeriodSeconds: 5EOF
The agnhost liveness image starts returning 500s on /healthz shortly after boot. With failureThreshold set to 1 and a probe-level grace period of 5 seconds, the wedged container is killed and restarted promptly instead of waiting out the 600-second pod grace period:
Liveness: http-get http://:8080/healthz ... #failure=1Warning Unhealthy Liveness probe failed: HTTP probe failed with statuscode: 500Normal Killing Container app failed liveness probe, will be restartedRESTARTS 3
A failing liveness probe with a probe-level terminationGracePeriodSeconds restarting the container promptly.
Cleaning up
When you are finished experimenting, remove everything you created:
kubectl delete pod grpc-liveness liveness-fast-restart named-port-probe -n probe-demokubectl delete namespace probe-demo
Summary
We covered how to configure probes in Kubernetes, the three probe types and the questions each one answers, the four methods (exec, TCP, HTTP, and gRPC) they use to check health, and the advanced settings that let you tune probe behavior to match your application. Used together, a liveness probe restarts containers that have wedged, a readiness probe keeps traffic away from pods that are not ready, and a startup probe protects slow-booting apps from premature restarts. Add the five tunables and a probe-level grace period on top, and you have the building blocks for highly available, self-healing deployments on Civo.

Technical Writer at Civo
Jubril Oyetunji is a DevOps engineer and technical writer with a strong focus on cloud-native technologies and open-source tools. His work centers on creating practical tutorials that help developers better understand platforms such as Kubernetes, NGINX, Rust, and Go.
As a contract technical writer, Jubril authored an extensive library of technical guides covering cloud-native infrastructure and modern development workflows. Many of his tutorials achieved strong search rankings, helping developers around the world learn and adopt emerging technologies.
Share this article
Further Reading
21 January 2025