Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
JSON-based Configuration for Kubernetes Deployments
Yes, you can deploy Kubernetes workloads from JSON files directly. kubectl apply accepts JSON and YAML, and the Kubernetes API ultimately works with JSON over HTTP. The practical rule is simple: JSON is excellent when manifests are generated, transformed, or validated by tooling; YAML is usually easier when humans are hand-editing files every day.
For most searchers, the real question is not whether JSON works, but how to use it without creating brittle Deployments. That means choosing the right labels, keeping runtime configuration out of the Deployment itself, validating against the API server before rollout, and avoiding stale guidance such as treatingkubectl convert like a built-in command on every machine.
Quick Takeaways
- Use
apps/v1for Deployments and set.spec.selectorexplicitly. - Make sure
.spec.selector.matchLabelsmatches.spec.template.metadata.labels, because Kubernetes rejects mismatches and the selector is immutable after creation inapps/v1. - Put environment-specific values in ConfigMaps and Secrets, then reference them from the Deployment.
- Validate with
kubectl apply --dry-run=server --validate=strictbefore a real apply. - Keep JSON for automation and APIs; keep YAML for manual authoring unless your team has a strong reason to standardize on JSON.
A Practical Deployment JSON Example
The example below is closer to what a real application Deployment looks like today. It uses stable labels, explicit selector matching, resource requests and limits, health probes, and references to external configuration instead of hard-coding everything into the manifest.
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "payments-api",
"namespace": "production",
"labels": {
"app.kubernetes.io/name": "payments-api",
"app.kubernetes.io/instance": "payments-api-prod",
"app.kubernetes.io/component": "api",
"app.kubernetes.io/managed-by": "kubectl"
}
},
"spec": {
"replicas": 3,
"revisionHistoryLimit": 10,
"selector": {
"matchLabels": {
"app.kubernetes.io/name": "payments-api"
}
},
"strategy": {
"type": "RollingUpdate",
"rollingUpdate": {
"maxUnavailable": "25%",
"maxSurge": "25%"
}
},
"template": {
"metadata": {
"labels": {
"app.kubernetes.io/name": "payments-api",
"app.kubernetes.io/instance": "payments-api-prod",
"app.kubernetes.io/component": "api"
}
},
"spec": {
"securityContext": {
"runAsNonRoot": true,
"seccompProfile": {
"type": "RuntimeDefault"
}
},
"containers": [
{
"name": "api",
"image": "ghcr.io/example/payments-api:v2.3.1",
"imagePullPolicy": "IfNotPresent",
"ports": [
{
"name": "http",
"containerPort": 8080
}
],
"envFrom": [
{
"configMapRef": {
"name": "payments-api-config"
}
},
{
"secretRef": {
"name": "payments-api-secrets"
}
}
],
"resources": {
"requests": {
"cpu": "100m",
"memory": "128Mi"
},
"limits": {
"cpu": "500m",
"memory": "512Mi"
}
},
"readinessProbe": {
"httpGet": {
"path": "/ready",
"port": 8080
},
"initialDelaySeconds": 5,
"periodSeconds": 10
},
"livenessProbe": {
"httpGet": {
"path": "/health",
"port": 8080
},
"initialDelaySeconds": 15,
"periodSeconds": 20
},
"securityContext": {
"allowPrivilegeEscalation": false,
"readOnlyRootFilesystem": true
}
}
]
}
}
}
}Why these fields matter
apiVersion: apps/v1is the current stable Deployment API you should author directly..spec.selector.matchLabelsand the Pod template labels must line up exactly. If they do not, the API rejects the Deployment.- The selector should be treated as permanent. In
apps/v1, changing it later is not a safe refactor path. - Shared labels under
app.kubernetes.io/*improve discoverability in dashboards, CLIs, and automation without changing how the Deployment works. - Resource requests and limits make scheduling and capacity behavior predictable instead of leaving it to cluster defaults.
- Readiness and liveness probes help Kubernetes distinguish between a Pod that is starting up and a Pod that is unhealthy.
- The image is pinned to a version tag instead of
:latest, which makes rollouts and rollbacks auditable.
Put App Configuration in ConfigMaps and Secrets
A Deployment should describe how Pods run, not carry every environment-specific value inline. In practice, JSON-based configuration for Kubernetes deployments works best when the Deployment references separate ConfigMaps for non-secret settings and Secrets for credentials or tokens.
ConfigMap Reference
{
"configMapRef": {
"name": "payments-api-config"
}
}Secret Reference
{
"secretRef": {
"name": "payments-api-secrets"
}
}- Use ConfigMaps for ports, feature flags, log levels, and other non-sensitive runtime settings.
- Use Secrets for database passwords, API keys, and tokens. Do not hard-code them inside the Deployment JSON.
- If your deployment pipeline generates JSON, generate these references too, rather than flattening all configuration into a single giant Deployment object.
Validate and Apply JSON Safely
The most useful kubectl workflow is to validate first, then apply, then inspect the live object in JSON if you need to debug exactly what the API accepted.
kubectl apply --dry-run=server --validate=strict -f deployment.json
kubectl apply --server-side -f deployment.json
kubectl get deployment payments-api -n production -o jsonkubectl apply -f deployment.jsonworks natively becausekubectl applyaccepts JSON and YAML.--dry-run=serversends the request to the API server without persisting it, which catches schema and admission problems that client-only checks can miss.--validate=stricthelps catch unknown or duplicate fields before they quietly turn into confusing rollout bugs.--server-sideis worth considering when multiple tools or controllers may touch the same object, because field ownership is tracked on the server.-o jsonis the easiest way to compare your intended manifest with the live object that Kubernetes is actually running.
About kubectl convert
Older guides often mention kubectl convert as if it ships with every kubectlinstall. Current Kubernetes documentation explicitly notes that the tool is not installed by default. For most teams, the better path is to author new Deployment manifests directly in apps/v1instead of building a workflow around conversion.
Common JSON Deployment Mistakes
- Selector mismatch: if
.spec.selector.matchLabelsdoes not match the Pod template labels, the API rejects the object. - Trying to rename the selector later: in
apps/v1, the Deployment selector is immutable after creation, so treat it as part of the object's identity. - Using live output as source without cleanup: JSON from
kubectl get -o jsonincludes fields such asstatus,resourceVersion, andmanagedFieldsthat do not belong in clean desired-state manifests. - Embedding secrets directly in the Deployment: it makes rotation harder and increases the blast radius of every manifest consumer.
- Relying on JSON by hand without formatting: missing commas, unquoted keys, or trailing characters break the entire document. Format and validate JSON before the apply step.
- Using floating image tags:
:latestmakes rollbacks and incident analysis harder than pinned versions or digests.
When JSON Is the Right Choice
Good fit for JSON
- Code-generated manifests in CI/CD pipelines
- Direct Kubernetes API integrations and client libraries
- Systems that already store configuration as JSON
- Debugging with exact live object output from the cluster
Usually better in YAML
- Hand-maintained app manifests in Git
- Reviews where humans need to scan nested objects quickly
- Files that benefit from inline comments and lighter syntax
- Teams without a strong tooling reason to standardize on JSON
If you do keep Deployment manifests in JSON, a formatter becomes part of the workflow, not an optional cleanup step. Consistent indentation and ordering make code review easier and reduce trivial syntax mistakes before kubectl ever sees the file.
Conclusion
JSON-based configuration for Kubernetes Deployments is fully supported and genuinely useful when manifests come from software instead of a human text editor. The safe pattern is to author Deployments inapps/v1, keep selectors stable, reference ConfigMaps and Secrets for runtime data, and validate against the API server before rollout.
If you only remember one thing, remember this: JSON is the machine-friendly format Kubernetes already speaks, but the quality of the Deployment still depends on structure, validation, and operational habits, not on the braces alone.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool