You wrote a Deployment, ran kubectl apply, and got The Deployment is invalid: spec.selector: Required value. The error fires at the API server — too late if you're mid-deploy or in a PR review. This validator parses your manifest in the browser and flags the common mistakes: missing apiVersion, kind, metadata.name; unknown kinds; Deployment without spec.selector; Service without ports; matchLabels that don't match template.metadata.labels. Multi-document files separated by --- are supported, with each document checked independently. Nothing is uploaded — paste a manifest containing Secrets and the values stay in your tab.
How to use
- Paste your manifest — single resource or multi-document YAML separated by
---. Output from helm template or kustomize build works directly. - Click Validate — each document is parsed and checked against the required-field heuristics for its
kind. - Read the per-document report — errors include the document index, the kind, and which field failed.
- Fix and re-validate, then run
kubectl apply --dry-run=server against a real cluster for full admission-chain validation.
Example 1: Catching a selector mismatch
A Deployment's spec.selector.matchLabels must exactly equal spec.template.metadata.labels. The selector is immutable after creation, so getting this wrong on day one means deleting and recreating later.
Manifest with bug
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: webserver # mismatch — selector won't match
spec:
containers:
- name: nginx
image: nginx:1.27Validator output
Document 1 (Deployment "web"):
✗ spec.selector.matchLabels does not match spec.template.metadata.labels
selector: { app: web }
template: { app: webserver }Example 2: API version skew
Older manifests use deprecated apiVersion strings that were removed in Kubernetes 1.22+. This is a frequent source of broken deploys after a cluster upgrade.
| Resource | Deprecated | Current | Removed in |
|---|
| Ingress | extensions/v1beta1 | networking.k8s.io/v1 | 1.22 |
| Deployment | extensions/v1beta1 | apps/v1 | 1.16 |
| CronJob | batch/v1beta1 | batch/v1 | 1.25 |
| PodSecurityPolicy | policy/v1beta1 | Replaced by Pod Security Admission | 1.25 |
| HorizontalPodAutoscaler | autoscaling/v2beta2 | autoscaling/v2 | 1.26 |
This validator flags the unknown kind. For a cluster-version-specific check, run pluto detect or kubent.
What this catches
- Missing required top-level fields —
apiVersion, kind, or metadata.name. - Unknown
kind — typos like Deploymen or non-standard CRDs not yet registered. - Deployment without selector —
spec.selector.matchLabels is mandatory and must match spec.template.metadata.labels. - Service without ports — empty
spec.ports array. - ConfigMap / Secret with non-string data — values must be strings (Secrets must additionally be base64-encoded).
- Multi-document YAML errors — bad
--- separators, mixed empty documents.
YAML specification reference
Quick reference from YAML 1.2. The converter enforces these rules on input; anything outside them is rejected or normalized.
| Element | Meaning | Example |
|---|
| Mapping | Block or flow key/value pairs | name: alice |
| Sequence | Ordered list with `- ` or flow `[...]` | - apple
- banana |
| Scalar | Plain, single-quoted, double-quoted, literal `|`, folded `>` | "hello\n" |
| Anchor / alias | & defines, * references; enables DRY configs | &base
*base |
| Tag | Explicit type: !!int, !!str, !!binary … | !!int "42" |
Common invalid forms
name:alice // missing space after colon
- item
-indented // inconsistent indent
flag: yes
value: 1 // tab used for indent (YAML forbids tabs)
Common use cases
- Pre-merge PR check — sanity-check Kubernetes YAML in a code review without spinning up a cluster.
- Helm chart rendering audit — validate the YAML produced by
helm template for an entire chart in one paste. - Kustomize output review — verify the result of
kustomize build before applying it. - Teaching — let learners iterate on resource definitions and see errors instantly without needing a cluster.
- CI pre-flight — fail fast in the pipeline before kubectl ever connects to a cluster.
Kustomize vs raw vs Helm — when to use which
| Approach | Best for | Trade-off |
|---|
| Raw YAML | Simple apps, prototypes, learning | Copy-paste leads to drift between environments |
| Kustomize | Environment overlays (dev/staging/prod patches) | Patch syntax is YAML-flavored, harder for newcomers |
| Helm | Reusable charts, parameterized templates, registries | Go templates inside YAML — heavy quoting headaches |
Validate the rendered output, not the templates — both helm template and kustomize build can emit subtly different YAML than what you read in the source file.
What this does NOT catch
Being upfront about the boundary helps you reach for the right tool. This validator intentionally trades depth for speed and privacy — it never leaves your browser. Several classes of issues need a server-side check or a real cluster connection:
- Custom Resource Definitions (CRDs) — Anything from Helm operators (cert-manager, Argo, Istio) or your own CRDs is treated as an unknown
kind. Use kubectl explain <crd> or kubeconform with the CRD JSON Schema registered. - Admission webhooks — Validating and mutating admission controllers (OPA Gatekeeper, Kyverno policies, PodSecurity admission) only run when
kubectl apply --dry-run=server talks to your live API server. - Deprecated API versions — Manifests with
extensions/v1beta1 Ingress or policy/v1beta1 PodSecurityPolicy compile fine here but fail on Kubernetes 1.22+. pluto or kubent detect these specifically. - Cluster-state-dependent checks — Whether a referenced
ConfigMap, Secret, or ServiceAccount actually exists; whether nodeSelector matches a node; whether storageClass is bound. - Resource quota / LimitRange enforcement — Namespace quotas reject applies at admission time. No way to check without the API.
Beyond static validation — escalation ladder
Once this validator passes, the next checks form a useful escalation ladder. Pick the lightest tool that catches your class of mistake.
| Tool | Checks | Needs cluster? |
|---|
kubectl apply --dry-run=client | Local YAML parse, basic schema (built-in resources only) | No |
kubeconform | OpenAPI schema validation per K8s version, supports CRDs | No |
kubectl apply --dry-run=server | Full admission chain (webhooks, quotas, defaulting) — no state mutation | Yes |
conftest test (OPA Rego) | Custom policy rules (no-root containers, image registry allowlist, label conventions) | No |
pluto / kubent | Deprecated API version detection | No |
Common YAML pitfalls in K8s manifests
- Tabs instead of spaces — YAML rejects tabs for indentation. Configure editors like VS Code to always insert spaces for
*.yaml files. The error is usually found character that cannot start any token. - The Norway problem — Unquoted
NO, YES, OFF, ON get parsed as booleans in YAML 1.1. countryCode: NO becomes countryCode: false. Always quote string values that could be confused. - Bare numeric strings losing precision — Port numbers and version strings like
8080 or 1.10 parse as numbers and may lose leading or trailing zeros. Quote them: port: "8080". - Anchors and aliases not always supported —
&anchor / *alias work in pure YAML parsers but break in some Kubernetes controllers that re-marshal manifests. Prefer Kustomize or Helm for DRY composition. - Multi-document files missing the trailing newline— Some YAML parsers (notably older Helm versions) drop the last document if the file doesn't end with a newline after the final
---. - Selector / template label mismatch — Deployment
spec.selector.matchLabels must exactly match spec.template.metadata.labels. The selector is immutable after creation. - String
true vs boolean true — Annotations require strings ("true"), but spec fields like privileged: true require booleans. Wrong type silently changes meaning.
Related guides
JSON vs YAML: When to Use What — A Developer's Guide
JSON wins on APIs; YAML wins on configs. Side-by-side syntax, parser behaviour, and where each fits across Kubernetes manifests, REST payloads, and GitHub Actions.
Docker Compose for Beginners: From docker run to YAML
How to translate a pile of docker run commands into a single docker-compose.yml. Service definitions, named networks, volumes, env files, and the override patterns for dev vs prod.
YAML Configuration Files: Syntax, Best Practices, and Common Pitfalls
YAML whitespace is unforgiving and the Norway problem (no:) still bites. Types, anchors, aliases, and the rules that keep Kubernetes manifests, GitHub Actions, and Helm values readable.
Kubernetes Manifest Validation: Catch Errors Before kubectl Apply
The cluster is the most expensive place to discover a YAML typo. Schema validation, server-side dry-run, CRDs, and the apiVersion graveyard.
Frequently Asked Questions
Q: Does this replace kubectl apply --dry-run?
No — it catches the early class of mistakes (missing fields, typos in kind, selector/label mismatch) without needing a cluster. Server-side dry-run still does things this can't: admission webhooks, mutating defaults, quota enforcement. Run this first in your editor, then kubectl apply --dry-run=server in CI for the deeper check.
Q: My CRDs are flagged as unknown — what now?
This validator only knows the built-in Kubernetes resource kinds. For CRDs ( cert-manager Issuer, Argo Application, Istio VirtualService, your own controllers), use kubeconform -strict -schema-location '{...}' with the CRD JSON Schema URLs. The flagged-as-unknown warning is informational, not an error — your cluster will accept the resource if the CRD is installed.
Q: Can I validate Helm chart templates directly?
Not the raw templates with {{ .Values.x }}placeholders — Go template syntax isn't valid YAML until rendered. Run helm template my-chart first and paste the rendered output. For chart-level linting, helm lint catches template syntax errors, and this tool catches the resulting resource-level errors.
Q: Does it check deprecated API versions?
Not explicitly. A deprecated extensions/v1beta1 Ingress manifest parses cleanly here because the structure is still valid YAML. Use pluto detect or kubent with your target cluster version to flag specific deprecations. See our Kubernetes manifest validation guide for the full workflow.
Q: Are Secret values exposed when I validate?
No. The manifest is parsed locally in your browser tab — base64-encoded Secret values never leave your machine. You can verify by opening DevTools and watching the Network tab: the page makes no outbound requests during validation. Safe to paste manifests from production.
Q: What about kustomize output?
Run kustomize build path/to/overlay and paste the result. Kustomize composes multiple resources into a multi-document stream (--- separated), which this validator handles natively. Each composed resource is validated independently.
Limitations
This is a fast heuristic validator, not a full OpenAPI schema check. It does not know about cluster-specific CRDs, version-specific field deprecations, or admission webhooks. For complete validation against your target cluster version, use kubeval, kubeconform, or kubectl apply --dry-run=server after this heuristic check passes.