Value over 63 characters
The limit that rejects an apply with an error naming the label, not the length
app.kubernetes.io/name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Paste manifests, or pairs one per line, and find out which keys and values the API server will refuse and why. A label value stops at 63 characters and has no room for a slash or a colon, which is why an image reference or a URL belongs in an annotation, and why the object with it in a label does not partly apply.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Validate. Nothing leaves this tab.
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.
The limit that rejects an apply with an error naming the label, not the length
app.kubernetes.io/name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
The prefix before the slash must be a DNS subdomain, so capitals are rejected
My-App/tier: frontend
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
The limit applies to the value, and a common source is a Git branch name or a full image reference pasted into a label.
Instead:Truncate deliberately, or move the long value to an annotation, which allows far more.
Label values cannot contain slashes or colons. Annotations can, and are the intended home for anything not used as a selector.
Instead:Labels are for selecting. Annotations are for everything else.
spec.selector on a Deployment is immutable after creation. Changing it requires deleting and recreating the Deployment.
Instead:Choose selector labels once, and keep changeable metadata in non-selector labels.
Every key and value in what you paste, against the rule for the half of the key it sits in, plus the limits that belong to the object rather than to the pair.
A label value is 63 characters of letters, digits, hyphens, underscores and dots. The slashes in a repository path and the colon before a tag are both outside that alphabet, so registry.example.com/team/api:1.4.0 breaks at three separate characters and takes the whole object down with it. A value that looks like an image reference or a URL is named as such in the finding, and the fix is the annotation rather than a shorter label.
An optional DNS subdomain prefix of up to 253 characters, a slash, then a name of up to 63. The prefix follows the DNS rules, lowercase with no underscores, while the name after the slash takes capitals, underscores and dots. So example.com/Team_Name is valid and Example.COM/team is not, and each half is reported against its own rule. A second slash is reported on its own, since there is nowhere for a third part to go.
Semver build metadata is where this usually lands: 1.4.0+build.27 in app.kubernetes.io/version is rejected, and the error only names the label. The suggested fix is the underscore form, 1.4.0_build.27, which is what the Docker tag beside it has to do for the same reason.
An annotation value is any string: no character rules and no per-value length. What it has is a shared limit of 262,144 bytes per object, summed across every key and every value, so the size check is done per object rather than per annotation. Anything past half of that is called out too, because kubectl apply stores the whole object as JSON in last-applied-configuration and that counts against the same total, so an object can be created and then fail to apply.
Labels and annotations are both map[string]string. An unquoted 1.24 arrives as a number and an unquoted true as a boolean, and the API server rejects the whole object with a Go unmarshalling error that never names the key. An empty value is the opposite case: legal, and not the same as the label being absent, so it is reported as information rather than a fault.
metadata.labels and metadata.annotations, the pod template's labels and annotations including a CronJob's jobTemplate, spec.selector.matchLabels, the keys and values inside matchExpressions, a Service's plain spec.selector, a NetworkPolicy's podSelector and a nodeSelector. Selector keys on a Deployment, StatefulSet, DaemonSet, ReplicaSet or Job also get a note that they are immutable, which is the one thing about a label that cannot be undone without deleting the object.
A label is an index key. The API server selects on it, so it is held to something short and comparable: 63 characters and a narrow alphabet. An annotation is somewhere to keep a string, nothing selects on it, and it has no alphabet or per-value limit at all. Almost every invalid label error is annotation-shaped data in a label: an image reference, a URL, a git subject line, a JSON blob.
The prefix before the slash is a DNS subdomain and follows the DNS rules, which means lowercase and no underscores. The name after the slash is wider than that: capitals, digits, hyphens, underscores and dots, beginning and ending on a letter or a digit. Same key, two alphabets, which is why an uppercase letter is a fault in one half and fine in the other. The prefix is optional, and a key with no slash is just the name part.
example.com/Team_Name
prefix example.com
optional, up to 253 characters
a DNS subdomain: lowercase, with dots and
hyphens and no underscores
name Team_Name
up to 63 characters
letters in either case, digits, "-", "_"
and ".", beginning and ending alphanumeric
Example.COM/team rejected, uppercase in the prefix
example.com/Team_Name valid
team/sub/owner rejected, a second slash At most 63 characters of letters in either case, digits, hyphens, underscores and dots, starting and ending with a letter or a digit. It may also be empty. Note what is not in there: no slash, no colon, no plus, no space, and no @ sign, which between them rule out every reference-shaped string anyone wants to store.
[A-Za-z0-9] and - _ . up to 63 characters
first and last character alphanumeric
Production valid, capitals are fine here
1.4.0 valid
"" valid, and not the same as absent
-1.4.0 rejected, cannot start with "-" Held up against the rule, an image reference fails at the registry separator, at every path separator and at the tag colon, and a digest reference adds an @ sign as well. There is no shortening that fixes it, because the characters are the problem rather than the length. The reference belongs in an annotation, and if you need to select on the version, the tag alone goes in the label.
registry.example.com/team/api:1.4.0
^ ^ ^
| | position 30: ":" before the tag
| position 26: "/" in the path
position 21: "/" after the registry
annotation example.com/image: registry.example.com/team/api:1.4.0
label app.kubernetes.io/version: 1.4.0 Semver build metadata is not a legal label value, and app.kubernetes.io/version is exactly where it lands. The same character is illegal in a Docker tag, so the image and the label fail together and only the label is mentioned in the error. The convention on both sides is the underscore.
1.4.0+build.27 rejected at position 6
1.4.0_build.27 valid
the tag has the same problem, and takes the
same repair The API server sums the key and the value of every annotation on an object and refuses anything over 256 KB with "metadata.annotations: Too long". There is no individual limit, which is why a single large annotation and fifty medium ones fail identically. The half-budget warning matters because kubectl apply writes a full JSON copy of the object into last-applied-configuration, inside the same budget, so the create succeeds and the next apply does not. Server-side apply tracks the same information in managedFields and writes no annotation.
sum of every key + every value <= 262,144 bytes
created with 200 KB of annotations fine
kubectl apply on the same object adds ~200 KB
of last-applied
and fails
kubectl apply --server-side no annotation An empty label value is valid and means something specific. Selecting on the key with no value matches only the objects where the key is set to empty. Selecting on the bare key matches anything carrying it whatever the value. Two different queries, and the difference is invisible in the manifest.
kubectl get pods -l tier= key present, value empty
kubectl get pods -l tier key present, any value
kubectl get pods -l '!tier' key absent spec.selector is immutable on an apps/v1 Deployment, StatefulSet, DaemonSet and ReplicaSet, and on a Job. Every key in it is fixed for the life of the object: an apply that changes one is rejected, and the way through is to delete and recreate, which for a Deployment means downtime you did not schedule. So the selector holds only what identifies the workload and never moves, and anything that does move, a version or a build number, goes on the pod template labels, which are free to change.
spec.selector.matchLabels
app.kubernetes.io/name: api fixed forever
app.kubernetes.io/instance: prod fixed forever
spec.template.metadata.labels
app.kubernetes.io/name: api
app.kubernetes.io/instance: prod
app.kubernetes.io/version: 1.4.0 free to change app.kubernetes.io carries name, instance, version, component, part-of and managed-by, plus created-by which is deprecated. Dashboards and tooling look for those exact strings, so partof instead of part-of is the worst kind of mistake: nothing errors, the label is perfectly valid, and the object simply never appears where you expected it. A key that is a recommended one with the punctuation or the case rubbed off is named as the probable typo it is, and any other key under the prefix is reported as being outside the set.
app.kubernetes.io/part-of recognised
app.kubernetes.io/partof valid, and invisible
app.kubernetes.io/team valid, and outside the set Those prefixes are reserved for Kubernetes core components and its subprojects. Nothing stops you setting one today, and a future release is free to define the same key and act on it. The keys Kubernetes actually defines, topology.kubernetes.io/zone and the rest, are recognised rather than flagged, because a false positive on a manifest that is doing nothing wrong costs more than the rule earns. What is left over is your own key wearing a Kubernetes domain, and the answer is a domain you control.
kubernetes.io/team=platform reserved prefix
topology.kubernetes.io/zone=eu-west-1a a key Kubernetes defines
example.com/team=platform yours Whether a selector matches anything. That needs both objects, the selector and the pods, and this reads the text in front of it: a valid selector that matches nothing is valid. It also does not know whether the values mean what you intend, and pairs pasted as a bare list are checked as labels, since a loose key=value line carries nothing that says it was an annotation.
checked keys, values, lengths, positions,
the per-object annotation budget
not checked whether the selector matches a pod
whether the value is the right value