I asked Copilot for a Deployment manifest. It produced valid YAML. kubeconform passed it against the strict schema without a complaint.
Then I ran a policy scanner over the same file and got twenty-one findings.
Nothing was wrong with the YAML. Everything was wrong with the Deployment.
Schema validation and safety are different questions
This is the part worth internalising, because it generalises past Kubernetes: a structural validator checks that your file is shaped correctly. It has no opinion about whether the thing you described is a good idea.
kubeconform asks "is spec.template.spec.containers[0].image a string?" It does not ask "does this container run as root?" Both tools were doing their jobs. Only one of them was doing the review.
Run both. The schema check is fast enough to be a pre-commit hook; the policy scan belongs in CI.
The three things generated manifests omit, essentially every time
Probes. No readinessProbe, no livenessProbe, no startupProbe.
Resource requests and limits. A pod with no requests is scheduled anywhere and evicted first.
securityContext. No runAsNonRoot, no allowPrivilegeEscalation: false, no readOnlyRootFilesystem, no dropped capabilities.
Those three are the review. If you check nothing else on a generated manifest, check those — and note that a namespace with Pod Security Admission set to restricted will reject the pod at admission rather than at review, which is a much better place to find out.
The probe mistake that turns a slow dependency into an outage
This one deserves its own section because it is subtle, common, and it fails at exactly the wrong moment.
Generated manifests frequently point livenessProbe and readinessProbe at the same endpoint. They look equivalent. They are not — they answer different questions:
-
startupProbe— has it finished booting? -
readinessProbe— should it receive traffic right now? -
livenessProbe— is it wedged, and worth killing?
If your /health endpoint checks the database, and the database gets slow, then a shared probe means every pod simultaneously fails liveness. Kubernetes responds by restarting your entire deployment during an incident instead of taking the pods out of rotation and letting them recover.
Readiness failing is "stop sending me traffic". Liveness failing is "I am beyond saving, kill me". Wiring them to the same check tells the cluster to do the second when you meant the first.
--dry-run=client is not offline
A very common and wrong assumption, and worth stating plainly because it breaks CI in a confusing way:
kubectl apply --dry-run=client -f manifests/
This downloads the OpenAPI schema from the API server. With no cluster reachable, it fails outright. It is not a local syntax check, and it is not what you want in a pipeline that has no cluster credentials. Use kubeconform for that.
And check your context before every command
kubectl config current-context
Ambient context is how a command meant for staging reaches production. This has nothing to do with AI and everything to do with the fact that generated commands are easy to paste quickly.
Where Copilot genuinely helps here
Not in producing the manifest — in explaining one. Pointing it at an inherited chart and asking what a particular block does, or why a probe is configured a certain way, is the use case that has consistently earned its keep for me. Reading unfamiliar YAML is slow and there is no glory in it.
It is also good at the mechanical transformations: converting a Deployment to a StatefulSet, adding an init container, expanding a Kustomize overlay.
The pattern is the same one everywhere: it is strong where the answer is determined by the input, and weak where the answer requires knowing what your cluster is actually like.
The fix that outlasts the prompt
Put the rules in .github/copilot-instructions.md rather than re-typing them:
- Every container sets resources.requests and resources.limits.
- securityContext on every pod and container: runAsNonRoot,
allowPrivilegeEscalation: false, readOnlyRootFilesystem, drop ALL.
- Images pinned by digest, not tag. :latest is not deployable here.
- Three probes, and they point at different endpoints.
That changes what comes back on every request, which re-prompting does not. It supplies information the model did not have rather than asking it to try harder.
The longer version — including the full findings list, the NetworkPolicy and PodDisruptionBudget defaults, and the conftest policies — is at GitHub Copilot for Kubernetes Manifests. There is a companion repository with the manifests and the policy tests if you want something to run.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.