Kubernetes YAML beautifier
Format, validate, and lint a manifest against 16 production rules. Convert between YAML and JSON. Everything runs in your browser — nothing is uploaded.
[
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "payment-service",
"labels": {
"app": "payment-service"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "payment-service"
}
},
"template": {
"metadata": {
"labels": {
"app": "payment-service"
}
},
"spec": {
"hostNetwork": true,
"containers": [
{
"name": "api",
"image": "registry.internal/payment-service:latest",
"ports": [
{
"containerPort": 8080
}
],
"env": [
{
"name": "DB_PASSWORD",
"value": "hunter2-in-plaintext"
}
],
"securityContext": {
"privileged": true
}
}
]
}
}
}
},
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "payment-service"
},
"spec": {
"selector": {
"app": "payment-service"
},
"ports": [
{
"port": 80,
"targetPort": 8080
}
]
}
}
]
The 16 checks, and why each one matters
These are static versions of the checks OpsKit's deployment-guard agent runs before a merge. The paid kit runs them against your live cluster, with the evidence attached.
image-latest-tagImage uses :latest or no tag
A mutable tag makes a rollout unreproducible — a restarted pod can pull different bytes than its siblings, and a rollback has nothing to roll back to.
plaintext-secret-envSecret-looking value in plain text
Credentials in env.value land in the manifest, in Git, and in `kubectl describe` output. Use secretKeyRef or an external secret store.
privileged-containerPrivileged container
privileged: true disables nearly every container isolation boundary. A compromise becomes a node compromise.
host-namespaceShares a host namespace
hostNetwork, hostPID, or hostIPC break the pod out of its own namespace and expose the node's interfaces and processes.
missing-resource-limitsNo memory or CPU limit
Without limits a single leaking container can evict its neighbours. It is also the reason most OOMKill investigations end in ambiguity.
missing-resource-requestsNo resource requests
The scheduler places pods by request, not usage. Missing requests produce BestEffort pods that get evicted first under pressure.
missing-probesNo readiness or liveness probe
Without a readiness probe a rollout sends traffic to a container that hasn't finished starting. Without liveness, a wedged process never restarts.
runs-as-rootMay run as root
runAsNonRoot is not set to true, so the container can run as UID 0 and a container escape starts with root on the node.
allow-privilege-escalationPrivilege escalation not blocked
allowPrivilegeEscalation defaults to true. Setting it false stops setuid binaries from gaining more privileges than the parent process.
hostpath-volumehostPath volume
A hostPath mount ties the pod to one node's filesystem and is a common path to node-level privilege escalation.
single-replicaSingle replica
One replica means every node drain, rollout, and eviction is downtime. Two is the smallest number that survives a drain.
capabilities-not-droppedLinux capabilities not dropped
Dropping ALL and adding back only what's needed shrinks the syscall surface a compromised process can reach.
writable-root-filesystemWritable root filesystem
readOnlyRootFilesystem: true stops an attacker from dropping tooling into the image at runtime. Mount an emptyDir for genuine scratch space.
missing-recommended-labelsMissing app.kubernetes.io/name
The recommended labels are what dashboards, cost tooling, and ownership resolution key on. Without them a workload is hard to attribute.
missing-namespaceNo explicit namespace
The object lands wherever kubectl's current context points. In a GitOps repo that is an accident waiting for a context switch.
missing-pdbNo PodDisruptionBudget
Without a PDB a node drain can take every replica down at once. A budget makes voluntary disruption respect your availability target.
A linter reads your file. OpsKit reads your cluster.
Static checks catch what you wrote. They can't tell you why the pod is in CrashLoopBackOff right now. OpsKit gives your AI agent a read-only connection to the live cluster — and proposes the fix as a GitOps PR.