A pod will not start
kubectl get pods shows you which of the three you have. They fail at different stages, which is useful: the state alone tells you how far the pod got.
kubectl get pods
kubectl describe pod <name>describe first, always. The events at the end of its output usually name the reason outright.
Pending
The pod exists but has not been placed on a node. It never started, so there are no logs to read.
Almost always your namespace quota is exhausted:
kubectl get resourcequota -o yamlCompare used against hard. If CPU or memory requests are at the limit, nothing more can be scheduled until something else stops or you raise the plan. You cannot raise the quota yourself.
Remember that the number that counts is the request, not what your pods actually consume, and that a container declaring no requests still gets a default. A namespace can be quota-exhausted while looking idle. See Billing and quotas.
Two less common causes, both visible in describe:
- The pod asks for more CPU or memory than the whole quota allows, so it can never fit.
- A PVC it mounts has not bound yet, see Storage and cost surprises.
ImagePullBackOff
The pod was placed, but the image could not be pulled. describe names the registry and the reason.
For a private registry, the credentials are missing or wrong. Your namespace usually has a pull secret already; check:
kubectl get secret itsh-registryIf it is there, reference it in the pod template, which is not automatic:
spec:
imagePullSecrets:
- name: itsh-registryIf it is missing, create one of type kubernetes.io/dockerconfigjson with a registry token from the portal.
For a public registry, it is nearly always the tag or the spelling. A tag that does not exist and a typo in the repository name produce the same manifest unknown.
On the free tier, remember that outbound traffic is limited to HTTP and HTTPS. Registries use HTTPS, so pulls work, but a registry on a non-standard port does not.
CrashLoopBackOff
The container starts and exits, repeatedly, with a growing delay between attempts. This is the friendliest of the three: it means scheduling and image pull both worked, so the problem is inside your application or its configuration.
The current log is from the newest attempt. To see the one that actually failed:
kubectl logs deployment/<name> --previousCommon causes, in rough order:
- A missing environment variable or secret. The application exits on startup. The log usually says which one.
- It cannot reach its database. On the free tier, check that the database is inside your namespace: outbound connections on database ports do not work there, see Outbound connections fail.
- It was killed for using too much memory.
describeshowsLast State: TerminatedwithReason: OOMKilled. Raise the memory limit, and remember a container that declares none is capped at a small default. - A liveness probe is failing. The application is fine but too slow to answer during startup, so it gets killed and restarted forever. Raise
initialDelaySeconds, or use a startup probe. - The container exits cleanly. Exit code 0 still restarts, because the default restart policy does not care why it stopped. A container that runs a task and finishes belongs in a Job, not a Deployment.
What's next
- Your first deployment for a manifest that works
- Billing and quotas for what fills the quota