Kubernetes Manifests Validation with Flux Schema CLI
The flux schema validate command validates Kubernetes YAML manifests from one
or more files or directories against a JSON Schema resolved from each
document’s apiVersion and kind.
Examples:
# Validate all YAML files in a directory tree
flux schema validate ./manifests --skip-missing-schemas
# Validate a Helm chart by piping the rendered output
helm template ./charts/app | flux schema validate --verbose
A non-zero exit code is returned when any document is invalid or errored.
Flags
| Flag | Description |
|---|---|
--schema-location | URL or file path for schemas (repeatable, tried in order); default points at the built-in catalog. |
--skip-missing-schemas | Skip documents for which no schema can be found. |
--skip-kind | Skip documents matching kind or apiVersion/kind (repeatable). |
--skip-json-path | Strip a JSON Pointer field before validation, optionally scoped: [apiVersion/kind:]/path (repeatable). |
--skip-file | Glob pattern matched against files and dirs; defaults to skipping dotfiles and dot-dirs (repeatable). |
--skip-cel-rules | Skip evaluation of x-kubernetes-validations CEL rules. |
--fail-fast | Exit after the first invalid document. |
--concurrent | Number of concurrent workers (default 8). |
--insecure-skip-tls-verify | Disable TLS certificate verification when fetching schemas over HTTPS. |
-v, --verbose | Print a line for every document, including valid and skipped ones. |
-o, --output | Output format, one of text, json or yaml (default: text). |
--config | Path to a YAML file supplying default values for validate flags (env: FLUX_SCHEMA_CONFIG). |
Schema location
When no --schema-location is given, validate uses the
Flux Schema catalog, which covers the latest Kubernetes and OpenShift APIs,
the stable channel of Gateway API, and the Flux ecosystem CRDs:
flux schema validate ./manifests
To validate against your own schemas generated by flux schema extract, pass
the directory as --schema-location. Bare paths and URLs are auto-expanded to
the catalog layout {{.Group}}/{{.Kind}}_{{.Version}}.json:
flux schema validate ./manifests --schema-location ./my-schemas
For a different layout, pass a full Go template ending in .json:
flux schema validate ./manifests \
--schema-location './schemas/{{.Kind}}-{{.GroupPrefix}}-{{.Version}}.json'
Template variables are .Group, .GroupPrefix, .Kind, and .Version.
The --schema-location flag is repeatable and locations are tried in order
(the first match wins). Pass the literal value default to include the
Flux Schema catalog alongside your own schemas:
flux schema validate ./manifests \
--schema-location default \
--schema-location https://raw.githubusercontent.com/datreeio/CRDs-catalog/main \
--schema-location './schemas/{{.Kind}}-{{.GroupPrefix}}-{{.Version}}.json'
See custom catalogs for guidance on building and hosting your own schema catalog.
Skipping documents and fields
Manifests can be piped in and certain documents skipped with --skip-kind:
kustomize build . | flux schema validate \
--skip-kind 'Service' \ # matches any Service kind regardless of apiVersion
--skip-kind 'source.toolkit.fluxcd.io/v1/ExternalArtifact'
Some manifests carry tooling-injected fields that are stripped at apply time
by Flux (e.g. SOPS-encrypted Secrets). Use --skip-json-path to remove those
fields from validation so the rest of the document is still checked:
flux schema validate ./manifests \
--skip-json-path 'v1/Secret:/sops' \
--skip-json-path 'Deployment:/sops'
Output
Default output (-o text) prints one line per document with its validation result,
and a summary at the end. To print valid documents and skipped ones alongside
invalid ones, pass --verbose:
$ flux schema validate ./manifests --verbose
manifests/releases.yaml - HelmRelease/apps/frontend is invalid: cel violation
- /spec: Invalid value: either 'chart' or 'chartRef' must be set
manifests/sources.yaml - Bucket/apps/frontend-config is invalid: schema violation
- /spec: missing property 'bucketName'
- /spec/interval: got number, want string
- /spec/secretRef/name: got object, want string
- /spec: additional properties 'force' not allowed
manifests/sources.yaml - OCIRepository/apps/frontend is invalid: yaml parse error
- line 10: key "app.kubernetes.io/name" already set in map
manifests/sources.yaml - HelmChart/apps/frontend is valid
manifests/sources.yaml - Secret/apps/auth-sops is skipped: kind skipped
Summary: 5 resources found in 2 files - Valid: 1, Invalid: 3, Skipped: 1
For CI pipelines and tooling, pass -o json (or -o yaml) to emit a
machine-readable report instead of plain text:
flux schema validate ./manifests -o json
Example JSON output:
{
"apiVersion": "schema.plugin.fluxcd.io/v1beta1",
"kind": "Report",
"$schema": "https://raw.githubusercontent.com/fluxcd/flux-schema/main/docs/report-v1beta1.json",
"report": {
"reporter": "flux-schema/v0.1.0",
"timestamp": "2026-05-20T12:00:00Z",
"summary": {
"total": 3,
"valid": 1,
"invalid": 2,
"skipped": 0
},
"results": [
{
"resource": {
"apiVersion": "v1",
"kind": "Namespace",
"name": "apps"
},
"source": "manifests/apps.yaml",
"idx": 1,
"status": "valid"
},
{
"resource": {
"apiVersion": "helm.toolkit.fluxcd.io/v2",
"kind": "HelmRelease",
"name": "frontend",
"namespace": "apps"
},
"source": "manifests/apps.yaml",
"idx": 2,
"status": "invalid",
"reason": "cel-violation",
"violations": [
{
"path": "/spec",
"message": "Invalid value: either 'chart' or 'chartRef' must be set"
}
]
},
{
"resource": {
"apiVersion": "source.toolkit.fluxcd.io/v1",
"kind": "OCIRepository",
"name": "frontend",
"namespace": "apps"
},
"source": "manifests/apps.yaml",
"idx": 3,
"status": "invalid",
"reason": "schema-violation",
"violations": [
{
"path": "/spec",
"message": "additional properties 'force' not allowed"
},
{
"path": "/spec/interval",
"message": "got number, want string"
}
]
}
]
}
}
See the
validation report reference for the full envelope
shape, the reason enum, and an example covering every result type. The
report is versioned by a published
JSON Schema.
Validation rules
- YAML documents with duplicate keys are rejected matching Flux behavior.
- Documents missing both
metadata.nameandmetadata.generateNameare flagged as invalid matching Kubernetes API behavior. metadata.name,generateName,namespace, andlabels/annotationskeys and values are checked against the Kubernetes API server’s ObjectMeta rules (DNS-1123, qualified names).- Schemas produced by
flux schema extract crdclose objects withadditionalProperties: false, so undocumented fields underspecfail validation. - String formats
duration,date,datetime/date-time, andtimeare validated matching Kubernetes OpenAPI conventions.
CEL validation rules
By default, the validate command extracts the x-kubernetes-validations
rules from the schemas and evaluates them as CEL expressions using the same
engine as the Kubernetes API.
CEL evaluation runs only after JSON Schema validation passes, and any rule
violations are reported with the cel-violation reason. Transition rules
referencing oldSelf evaluate with no prior state, matching the Kubernetes
API behavior on CREATE.
The CEL validation can be disabled with the --skip-cel-rules flag.
Config file
Flag values can be pre-set in a YAML config file and referenced with--config
or with the FLUX_SCHEMA_CONFIG environment variable. This keeps long invocations
out of CI scripts and makes validation reproducible across environments.
flux schema validate ./manifests --config .fluxschema.yml
The file is wrapped in a Config API envelope and has a validate section
for validation defaults. The shape is documented by the
Config JSON Schema.
apiVersion: schema.plugin.fluxcd.io/v1beta1
kind: Config
validate:
schemaLocation:
- default
- https://raw.githubusercontent.com/datreeio/CRDs-catalog/main
skipKind:
- source.toolkit.fluxcd.io/v1/ExternalArtifact
skipJSONPath:
- Secret:/sops
skipFile:
- '.*'
- kustomization.yaml
skipCELRules: false
skipMissingSchemas: false
verbose: true
failFast: false
concurrent: 8
insecureSkipTLSVerify: false
output: text
Rules:
- CLI flags override config values. Setting
--verbose=falsewins oververbose: truein the file. - Setting
--configoverridesFLUX_SCHEMA_CONFIG. When both are set, the flag wins and the env var is ignored. - Manifest paths stay positional. The config file configures how to validate; paths are given on the command line.
Running in CI
GitHub Actions
See the validate action reference.
Docker
The ghcr.io/fluxcd/flux-schema image bakes the
default catalog
into /catalog/latest/, so manifests can be validated offline without fetching schemas from GitHub.
Mount the manifests directory and point --schema-location at the builtin catalog:
docker run --rm \
-v "$PWD/manifests:/manifests:ro" \
ghcr.io/fluxcd/flux-schema:latest validate /manifests \
--schema-location /catalog/latest
Or pipe rendered manifests into the container over stdin
(note the -i flag to keep stdin open):
kustomize build ./manifests | docker run --rm -i \
ghcr.io/fluxcd/flux-schema:latest validate \
--schema-location /catalog/latest \
--skip-missing-schemas
Notes:
- Use
--schema-location /catalog/latestrather thandefaultfor air-gapped environments. - Pin the image to a release tag e.g.
ghcr.io/fluxcd/flux-schema:v0.3.0. - Tu use your own schema catalog, repeat
--schema-locationwith another mounted directory.