BackendBench
Coffee
Doctor

Kubernetes manifest doctor

Paste one or many Kubernetes manifests. The doctor flags missing resource limits and probes, latest images, privileged or root containers, host namespaces, plain-text secrets and API versions that no longer exist.

Everything runs in your browser. Nothing you paste is uploaded. Also try the paste-anything box or the AI-output verifier.

What the Kubernetes YAML doctor checks

A manifest that applies cleanly is not necessarily safe or stable. Without resource limits one pod can starve a node, without probes a deadlocked JVM keeps receiving traffic, and a privileged container turns a small bug into a node takeover. The rules follow the Pod Security Standards and the usual production readiness checklist, and every finding names the exact field to change.

There are 25 rules in this doctor: 11 high severity, 6 medium and 8 low. Each rule has a stable anchor, so you can link a teammate straight to the explanation, for example /doctor/k8s/#rule-privileged.

All 25 rules

HighPrivileged container#

Why it matters. privileged: true disables almost all isolation: the container can see host devices and escape to the node.

Fix. Remove it. Add only the specific capabilities you need. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2
          securityContext:
            privileged: true

HighContainer may run as root#

Why it matters. runAsUser: 0, or no runAsNonRoot at all, means a bug in the app is a root process inside the container.

Fix. Set securityContext.runAsNonRoot: true and a numeric runAsUser such as 10001. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2
          securityContext:
            runAsUser: 0

MediumallowPrivilegeEscalation not disabled#

Why it matters. The default lets a process gain more privileges than its parent, through setuid binaries for example.

Fix. Set securityContext.allowPrivilegeEscalation: false. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2
          securityContext:
            runAsNonRoot: true

LowRoot filesystem is writable#

Why it matters. A read-only root filesystem stops attackers dropping tools and makes the app declare where it writes.

Fix. Set readOnlyRootFilesystem: true and mount an emptyDir at /tmp. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

LowLinux capabilities are not dropped#

Why it matters. Containers start with a default set of capabilities most apps never use.

Fix. Add securityContext.capabilities.drop: ["ALL"] and add back only what is required. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

HighDangerous capability added#

Why it matters. SYS_ADMIN, NET_ADMIN, SYS_PTRACE and ALL are close to root on the node.

Fix. Remove the capability or use a narrower one such as NET_BIND_SERVICE.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2
          securityContext:
            capabilities:
              add: ["SYS_ADMIN"]

HighNo resource limits#

Why it matters. Without a memory limit a leaking pod can take down the node, and without a CPU limit it can starve neighbours.

Fix. Set resources.limits.memory (and cpu if you use CFS quotas) and keep the JVM heap well below it. K8s resources and HPA

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

MediumNo resource requests#

Why it matters. The scheduler packs pods blindly, and Kubernetes gives the pod the weakest quality-of-service class (BestEffort), so it is evicted first.

Fix. Set resources.requests for cpu and memory from real measurements. K8s resources and HPA

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

MediumImage uses latest or no tag#

Why it matters. A mutable tag means two pods of the same Deployment can run different code, and a rollback cannot reproduce the old version.

Fix. Pin a version tag or, better, an @sha256 digest. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app

MediumNo liveness probe#

Why it matters. A deadlocked JVM keeps running and receiving traffic; Kubernetes only restarts what it can detect.

Fix. Add livenessProbe against a cheap endpoint such as /actuator/health/liveness. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

MediumNo readiness probe#

Why it matters. Without readiness, traffic arrives while Spring is still starting and during every rollout, producing errors.

Fix. Add readinessProbe (for Spring Boot: /actuator/health/readiness) and a startupProbe for slow starts. K8s manifest generator

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

HighPod uses the host network#

Why it matters. hostNetwork puts the pod on the node network namespace, bypassing network policies.

Fix. Remove it unless this is a node-level agent.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      hostNetwork: true
      containers:
        - name: a
          image: a:1

HighPod shares host PID or IPC namespace#

Why it matters. hostPID and hostIPC let the pod see and signal host processes.

Fix. Remove hostPID and hostIPC.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      hostPID: true
      containers:
        - name: a
          image: a:1

HighhostPath volume#

Why it matters. Mounting a node directory exposes node files and ties the pod to one node.

Fix. Use a PersistentVolumeClaim, emptyDir, configMap or secret.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: a
          image: a:1
      volumes:
        - name: v
          hostPath:
            path: /var

HighDeprecated or removed apiVersion#

Why it matters. These API versions were removed in recent clusters, so kubectl apply fails with "no matches for kind".

Fix. Switch to the stable version (apps/v1, batch/v1, networking.k8s.io/v1, autoscaling/v2) and add the now required fields such as spec.selector. K8s manifest generator

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: web

MediumSecret value written in env#

Why it matters. A literal password in the manifest lands in git and in kubectl describe output.

Fix. Use valueFrom.secretKeyRef (and an external secret store).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2
          env:
            - name: DB_PASSWORD
              value: hunter2

LowSingle replica#

Why it matters. One replica means every rollout, node drain or crash is downtime.

Fix. Run at least 2 replicas with a PodDisruptionBudget and anti-affinity or topology spread. K8s resources and HPA

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: a
          image: a:1

LowDeployed to the default namespace#

Why it matters. The default namespace has no isolation or quotas and becomes a dumping ground.

Fix. Give each application its own namespace.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: default

HighSelector does not match the pod template labels#

Why it matters. The API server rejects the Deployment, or a Service sends traffic to nothing.

Fix. Make spec.selector.matchLabels a subset of spec.template.metadata.labels.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: other
    spec:
      containers:
        - name: a
          image: a:1

HighBinding to cluster-admin#

Why it matters. cluster-admin is full control of the cluster; a compromised pod using this account owns everything.

Fix. Create a Role with only the verbs and resources needed and bind that.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: x
roleRef:
  kind: ClusterRole
  name: cluster-admin

LowAPI token mounted into the pod#

Why it matters. Most apps never call the Kubernetes API, yet every pod gets a token an attacker can use.

Fix. Set automountServiceAccountToken: false on the pod spec.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: app
          image: app:1.2

LowCronJob without concurrencyPolicy#

Why it matters. The default Allow lets a slow run overlap with the next, which duplicates work.

Fix. Set concurrencyPolicy: Forbid (or Replace) and startingDeadlineSeconds. K8s CronJob generator

apiVersion: batch/v1
kind: CronJob
metadata:
  name: j
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: a
              image: a:1
          restartPolicy: OnFailure

LowIngress without TLS#

Why it matters. Traffic and cookies cross the network in clear text.

Fix. Add spec.tls with a certificate secret (cert-manager can issue it).

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: i
spec:
  rules:
    - host: a.example.com

LowNodePort service#

Why it matters. NodePort opens a high port on every node, bypassing the ingress and its authentication.

Fix. Use ClusterIP behind an Ingress or a LoadBalancer.

apiVersion: v1
kind: Service
metadata:
  name: s
spec:
  type: NodePort
  ports:
    - port: 80

HighThe YAML does not parse#

Why it matters. kubectl apply fails before anything reaches the cluster.

Fix. Fix the indentation, tabs and quoting at the reported line. YAML to JSON

kind: Pod
spec:
  a: 1
 b: 2

Frequently asked questions

Does it handle multi-document YAML and Lists?

Yes. Documents separated by --- and kind: List items are all checked, and findings name the workload and container.

Which workload kinds are inspected?

Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob and Pod. Services, Ingress and RBAC objects get their own rules.

Does it replace kubeval, kube-linter or Kyverno?

No. It is a fast, private first pass in the browser. Use a policy engine in the cluster for enforcement and schema validation in CI.

Why are probes flagged on a DaemonSet or Job?

They are not. Readiness is skipped for DaemonSets and probes are only expected on long-running serving workloads.

More tools you might need