KubernetesWritten by GLWS15 min readUbuntu 24.04 LTS / AlmaLinux 9
Commands run on your server unless a section says otherwise. Replace example usernames, addresses and paths with your own values. Keep a working session open when changing remote access.

Start with the same three commands every time

Most Kubernetes problems show up in a pod's status, and nearly all of the explanation is in two places: the events at the bottom of the describe output, and the container logs. Learn this routine and you can solve most failures without guessing.

Both systems (replace the namespace and pod name)
kubectl get pods -A
kubectl -n demo describe pod POD_NAME
kubectl -n demo logs POD_NAME
kubectl -n demo get events --sort-by=.lastTimestamp | tail -n 20
Read the Events section at the end of describe first. It usually names the exact problem in plain words.

Pending: the pod was never scheduled

A Pending pod has been accepted by the cluster but not placed on a node. The describe events say why. The common reasons are not enough CPU or memory on any node ("Insufficient memory"), a PersistentVolumeClaim that cannot bind, a taint or node selector that no node satisfies, or a missing image pull secret in some setups.

Both systems
kubectl -n demo describe pod POD_NAME | sed -n '/Events:/,$p'
kubectl describe node | grep -A8 "Allocated resources"
kubectl top nodes
kubectl -n demo get pvc

Pending because of resources

Kubernetes schedules by what a pod requests, not what it uses. If the requests of all pods add up to more than the node offers, new pods wait. On a small server, lower requests that are bigger than needed, remove pods you do not need, or move to a larger plan. Setting very high requests "to be safe" is a common cause.

Both systems
kubectl get pods -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,CPU:.spec.containers[*].resources.requests.cpu,MEM:.spec.containers[*].resources.requests.memory
kubectl -n demo set resources deployment/site --requests=cpu=50m,memory=64Mi

Pending because of storage

A claim stays Pending when no volume can satisfy it: a wrong storage class name, a size that is not available, or a class that waits for the first pod to use it. K3s's local-path class intentionally waits, so a claim shows Pending until a pod mounts it. If both the pod and the claim stay Pending, read the claim's events.

Both systems
kubectl get storageclass
kubectl -n demo describe pvc site-data | sed -n '/Events:/,$p'

ImagePullBackOff and ErrImagePull

The node could not download the image. The event message names the reason: the image name or tag does not exist, the registry needs a login, a rate limit was hit, or the node cannot reach the registry. Check the exact image string for typos, check that the tag exists, and test the pull from the node itself.

Both systems
kubectl -n demo describe pod POD_NAME | grep -i -E "image|pull|failed"
kubectl -n demo get pod POD_NAME -o jsonpath='{.spec.containers[*].image}'; echo
sudo k3s crictl pull nginx:stable
curl -sI https://registry-1.docker.io/v2/ | head -n 1
getent hosts registry-1.docker.io
RECORDED LAB OUTPUT · Ubuntu 24.04 LTS
      Reason:       ErrImagePull
  Warning  Failed     1s    kubelet            spec.containers{nginx}: Failed to pull image "nginx:glws-invalid-tag-validation": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/nginx:glws-invalid-tag-validation": failed to resolve reference "docker.io/library/nginx:glws-invalid-tag-validation": docker.io/library/nginx:glws-invalid-tag-validation: not found
  Warning  Failed     1s    kubelet            spec.containers{nginx}: Error: ErrImagePull
  Normal   BackOff    0s    kubelet            spec.containers{nginx}: Back-off pulling image "nginx:glws-invalid-tag-validation"
  Warning  Failed     0s    kubelet            spec.containers{nginx}: Error: ImagePullBackOff
Recorded fault injection using an intentionally invalid Nginx image tag. Diagnose the image reference before deleting the pod.

Pulling from a private registry

A private image needs credentials stored as a secret of type docker-registry, referenced from the pod or the service account. Create it without leaving the password in your shell history, and reference it as an image pull secret.

Both systems
read -rsp "Registry password or token: " REGPASS; echo
kubectl -n demo create secret docker-registry regcred --docker-server=ghcr.io --docker-username=YOUR_USER --docker-password="$REGPASS"
unset REGPASS
kubectl -n demo patch serviceaccount default -p '{"imagePullSecrets":[{"name":"regcred"}]}'

CrashLoopBackOff: the container keeps dying

The container starts, exits and is restarted with growing delays. The reason is in the container's own output, so read the logs of the previous run, because the current one may be empty. Then look at the exit code and the last state in describe.

Both systems
kubectl -n demo logs POD_NAME --previous
kubectl -n demo describe pod POD_NAME | grep -A8 "Last State"
kubectl -n demo get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].lastState}'; echo

Read the exit code

Exit code 1 or another small number is the application failing, so the logs explain it: a missing setting, a wrong password, a port already in use or a failed migration. Exit code 137 with reason OOMKilled means the container went over its memory limit and was killed, so raise the limit or fix the leak. Code 139 is a crash such as a segmentation fault. A container that exits with 0 immediately has simply finished, which is wrong for a service, so check its command.

Both systems
kubectl -n demo get pod POD_NAME -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason} {.status.containerStatuses[0].lastState.terminated.exitCode}'; echo
kubectl -n demo set resources deployment/site --limits=memory=512Mi

Crashes that come from probes and configuration

A liveness probe that fails restarts a healthy but slow application. Raise initialDelaySeconds or use a startup probe. Missing ConfigMaps and Secrets block a container from starting with a "CreateContainerConfigError" status, so check that every referenced object exists in the same namespace. Environment values and mounted files can be inspected inside a running container with exec.

Both systems
kubectl -n demo get configmap,secret
kubectl -n demo describe pod POD_NAME | grep -i -E "liveness|readiness|probe"
kubectl -n demo exec -it POD_NAME -- sh
kubectl -n demo run debug --rm -it --image=busybox:stable --restart=Never -- sh

Check the node and the control plane

If many pods misbehave at once, look at the node: memory or disk pressure evicts pods, and a full disk stops image pulls. On K3s, the service log shows cluster-level errors.

Both systems
kubectl describe node | grep -A10 "Conditions:"
df -h /
free -h
sudo journalctl -u k3s --since "30 minutes ago" --no-pager | tail -n 30
The guides on full disks and out-of-memory kills cover the host side of these problems.

Official documentation

These upstream references provide more detail on the commands and workflows in this guide.