kubectl annotate --overwrite: Usage, Options & Common CI Errors
Update an annotation idempotently across pipeline re-runs.
kubectl annotate --overwrite replaces an existing annotation's value, making annotation idempotent in CI. Annotations carry free-form metadata - change-cause, scrape hints, descriptions - that selectors ignore but tooling reads.
What it does
kubectl annotate RESOURCE NAME key=value --overwrite replaces an existing annotation (without it, re-annotating an existing key errors, exactly like label). key- removes one. Annotations have no value-format restriction and a generous size budget, so they hold URLs, JSON, and prose that labels cannot.
Common usage
kubectl annotate deploy/web kubernetes.io/change-cause="deploy ${GIT_SHA}" --overwrite
kubectl annotate svc/web prometheus.io/scrape="true" --overwrite
kubectl annotate pod my-pod description="debug fixture" --overwrite
kubectl annotate pod my-pod description- # removeCommon errors in CI
Re-running without --overwrite fails "already has a value ... and --overwrite is false" - add it for idempotent deploys. The total annotations payload is capped at 256 KB, so stuffing a large blob in triggers "metadata.annotations: Too long: must have at most 262144 bytes". Note client-side kubectl apply stores prior state in the kubectl.kubernetes.io/last-applied-configuration annotation, so very large manifests can approach that ceiling - prefer server-side apply, which does not use that annotation. The change-cause annotation is what populates rollout history's CHANGE-CAUSE column.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes