DevTools Hub

Search tools

Search for a developer tool

Common Kubernetes Manifest Errors

Part of the Kubernetes Toolkit

Most of these get rejected by kubectl apply outright — no cluster required to catch them, just knowing what the API server is actually going to check. A couple are worse: they apply cleanly and just quietly don't do what you expect.

An apiVersion Kubernetes has actually removed

apiVersion: extensions/v1beta1  # removed in 1.16
kind: Deployment

Kubernetes deprecates an API version, then removes it entirely a few releases later — at that point no server accepts it, full stop, regardless of how long the manifest has "worked." A few of the most commonly hit: extensions/v1beta1 and apps/v1beta1/apps/v1beta2 (removed in 1.16 — use apps/v1), networking.k8s.io/v1beta1 and rbac.authorization.k8s.io/v1beta1/v1alpha1 (removed in 1.22), and batch/v1beta1 and policy/v1beta1 (removed in 1.25, the latter for PodDisruptionBudgetPodSecurityPolicy, the other thing that lived at that path, has no replacement kind at all; it was removed outright). A manifest copied from an old tutorial or an old copy of your own repo is the most common way to hit one of these.

apiVersion and kind that don't match

apiVersion: v1        # wrong — Deployment lives under apps/v1
kind: Deployment

Every kind has exactly one (or occasionally two, during a migration window) correct apiVersion it actually lives under — Deployment, StatefulSet, DaemonSet, and ReplicaSet are all apps/v1; Job and CronJob are batch/v1; core objects like Pod, Service, and ConfigMap are plain v1. Mismatching them — an easy mistake when copy-pasting between resource types — gets rejected as an unrecognized kind-under-that-version combination.

An uppercase letter in a resource name

metadata:
  name: MyApp   # rejected — uppercase not allowed

Kubernetes resource names follow the DNS-1123 subdomain convention: lowercase alphanumeric characters, -, and . only, starting and ending with an alphanumeric character — no uppercase, ever, in any resource's name. This trips up people constantly coming from ecosystems where PascalCase or camelCase identifiers are normal. my-app works; MyApp and myApp both don't.

Services follow an even stricter naming rule

Every other resource kind allows a name to start with a digit (3-day-batch is valid for a Job). A Service name specifically follows the stricter DNS-1035 label format and must start with a letter — this exists because a Service name becomes part of a DNS label and an environment variable name, both of which have their own leading-character restrictions. It's also capped at 63 characters, where most other resource names allow up to 253.

Missing metadata, apiVersion, or kind entirely

All three are required on every object, with no exceptions — a manifest missing any of them (easy to do when hand-assembling YAML from fragments) is rejected before Kubernetes even looks at what kind of object it's supposed to be.

The same resource defined twice in one file

A multi-document file (----separated) with the same kind, namespace, and name appearing in two documents doesn't create one resource then update it — kubectl apply -f processes the whole file, and the second definition either overwrites the first silently or the apply fails, depending on what else differs between them. Easy to introduce when consolidating several previously-separate files into one and not noticing an overlap.

A Service with no selector — silently routes nowhere

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - port: 80
  # no "selector" — this Service has no endpoints

This one is dangerous specifically because it doesn't error. A Service with no selector applies cleanly and creates a real object — it just never gets any Endpoints, so every request to it fails to connect, and there's nothing in the manifest itself that flags this as wrong. (The one legitimate case: a Service of type: ExternalName, or one whose Endpoints/EndpointSlice are managed manually — both real, both rare.) If a Service's traffic is silently going nowhere, checking for a missing or typo'd selector before touching networking config is usually faster.

Duplicate container names within one pod

Container names only need to be unique within their own pod, but a duplicate there breaks anything that addresses a container by name — kubectl logs -c <name>,readinessProbe/livenessProbe results, and per-container status in kubectl describe all become ambiguous about which of the two containers they're actually referring to.

A resource quantity that doesn't parse

resources:
  limits:
    memory: "512mb"   # not a valid Kubernetes quantity — "Mi" is

Kubernetes quantities use their own suffix set — Ki/Mi/Gi/Ti for binary (1024-based) units, k/M/G/T for decimal, and m for milli — not the mb/gb suffixes common elsewhere. 512mb isn't a recognized quantity at all; 512Mi (mebibytes) is what was almost certainly meant.

Try it yourself

Kubernetes YAML Validator checks every mistake above — removed and mismatched apiVersions, invalid names, missing fields, duplicate resources, a Service with no selector — before kubectl apply ever sees the file. Kubernetes Manifest Formatter cleans up the YAML itself, and K8s Diff compares two versions of a manifest field-by-field. All three run entirely in your browser.

Related tools