Skip to content

Label Selector Tester · Kubernetes

See which pods your selector matches — and why.

Paste the pods and the selector — a kubectl -l string or a matchLabels / matchExpressions block — and get a per-resource verdict with the exact clause that decided it. Including the rule almost everyone answers backwards: NotIn and != match a resource that has no such label at all.

Runs in your browser — nothing you paste leaves this page. How we prove that

Runs in your browser No cluster needed No signup Updated Jul 31, 2026

Kubernetes Label Selector Tester playground

Examples
Selector grammar

Equality-based clauses (=, !=) and set-based ones (in, notin, a bare key, !key) can be mixed freely in one -l string — commas mean AND. A Deployment's spec.selector accepts both forms but is immutable after creation; a Service's spec.selector is a plain label map and supports equality only.

resources.yaml input

Results update as you type — press Enter to run now.

Paste one manifest, a --- stream, a kind: List or a YAML list. Press Esc to release keyboard focus from the editor; /Ctrl + Enter runs and leaves it. Nothing you paste is uploaded.

Verdicts

Paste the resources above — or tap an example — to see, per resource, whether the selector matches it and which clause decided.

The Gap

A selector that matches nothing is not an error anywhere in Kubernetes.

kubectl apply accepts it. The API server accepts it. No controller logs a warning, no event is emitted, no field is marked invalid. The only symptom of a Service whose selector misses its pods by one character is an Endpoints object with no addresses — and by the time you are looking at that, you are already debugging in the wrong place. The same silence covers a NetworkPolicy whose podSelector selects nothing, which fails open in exactly the way a security control should not.

The rules themselves are the second half of the problem, because two of them read backwards. env!=prod matches a pod that has no env label at all — NotIn is satisfied by an absent key, by design, in one branch of Requirement.Matches. And an empty selector matches everything, while a nil selector matches nothing. Both facts are one line of Go each, and both are routinely remembered the other way round.

Which is why this is a tool and not a blog post. Ask an assistant "does env!=prod match a pod with no env label" and you will get a fluent answer with a fifty-fifty chance of being wrong — and generated manifests routinely put matchExpressions inside a Service's spec.selector, a field that only accepts a plain label map. Paste the claim in here and the answer comes from the same semantics apimachinery implements, with the reason attached, in a tab that sends nothing anywhere.

Sizing the workload rather than selecting it? Kubernetes Resource Calculator totals requests and limits across replicas, and Env Example Checker catches the variables a container expects but never gets.

The Pipeline

How it works.

Four steps, all inside your browser tab, re-run as you type.

  1. Parse the selector the way apimachinery does.

    The `-l` grammar goes through a port of its own lexer and parser — context-sensitive, so `op in (in,notin)` is legal, and whitespace-tolerant, so `env in ( prod , dev )` is the same selector. Structured selectors go through `LabelSelectorAsSelector`'s rules instead.

  2. Read whatever is in the clipboard.

    One manifest, a `---` stream, a `kind: List`, or a YAML list. A workload's `spec.template.metadata.labels` becomes its own row, because a Service selects pod labels and not the Deployment's own.

  3. Evaluate clause by clause.

    `Requirement.Matches`, once per resource per clause: `In` needs the key present, `NotIn` is satisfied by an absent key, `Exists` accepts an empty value, `DoesNotExist` does not.

  4. Show the reason, not just the verdict.

    Every clause carries the sentence that decided it, naming the actual label value. The absent-key matches get an amber annotation, because those are the ones people and language models answer backwards.

Reference

Every operator, and what an absent key does.

Seven ways to write a clause, four operators behind them, and the column that is almost never printed next to the syntax: what happens when the resource does not carry the key at all. Three of the seven match in that case.

Written as Operator Structured form Key absent
key=value In matchLabels: {key: value} One value. `=` and `==` are the same operator. no match
key==value In matchLabels: {key: value} Identical to `=`; only the spelling is preserved. no match
key!=value NotIn operator: NotIn, values: [value] The one people get backwards. An absent key satisfies it. MATCHES
key in (a,b) In operator: In, values: [a, b] At least one value is required; the set is de-duplicated and sorted. no match
key notin (a,b) NotIn operator: NotIn, values: [a, b] Same absent-key rule as `!=`, because it is the same operator. MATCHES
key Exists operator: Exists Any value, including the empty string. Values are forbidden. no match
!key DoesNotExist operator: DoesNotExist Fails if the key is present with an empty value — it is still present. MATCHES

"Has the label, and it is not prod"

One clause cannot say that. env!=prod also picks up every resource with no env label. Add Exists next to it — commas mean AND.

BASH
# matches unlabelled pods too
kubectl get pods -l 'env!=prod'

# has an env label, and it is not prod
kubectl get pods -l 'env,env!=prod'

The empty selector, three ways

Empty means everything; nil means nothing; and a Service omits the field when it is empty, which leaves it with no managed endpoints at all.

YAML
# every pod in the namespace
podSelector: {}

# every resource, again
selector:
  matchLabels: {}

# a Service with NO selector: endpoints
# are yours to manage by hand
spec:
  ports: [{ port: 80 }]

The Fence

What it deliberately does not do.

Each of these was considered and refused, because the alternative was a plausible answer that could be wrong. A tester you cannot audit is a tester you have to trust.

Field selectors

A different mechanism with a per-kind, per-version allow-list of indexed fields, mostly without set-based operators. Simulating it offline would mean guessing which fields your cluster indexes.

Node affinity Gt and Lt

They belong to NodeSelectorRequirement, not LabelSelectorRequirement. Typing replicas>2 gets a message that says so instead of a comparison a label selector cannot express.

namespaceSelector semantics

A NetworkPolicy peer combines a namespaceSelector with a podSelector across namespaces. Without the namespace objects, the honest answer is the podSelector on its own.

Anything that needs the cluster

No API server is contacted, so no live pod list, no admission result and no endpoints object. Everything here is derived from the text you pasted.

Limits, stated rather than hidden: up to 500 resource documents from a paste of at most 500,000 characters, and at most 100 clauses in one selector. A label key name is validated at 63 characters, its optional DNS-subdomain prefix at 253, and a label value at 63 — the same limits the API server enforces.

Next Step

Paste the trace into the incident channel.

"Copy report" gives you the whole run as plain text: the canonical selector, then one block per resource with its verdict and every clause that decided it. Then keep going — total what the workload actually reserves, or check the variables its containers expect.

selector-report.txt
Kubernetes Label Selector Tester — 3 of 5 resources match
selector: app=web,tier=frontend

NO MATCH — Pod/web-d
  metadata.labels: app=web, tier=frontnd
  [ok] app=web — label app="web" = "web"
  [no] tier=frontend — label tier="frontnd" ≠ "frontend"

FAQ

Questions, answered.

Tap a question to expand the answer.

Because that is what the rule says, and it is the single most surprising line in the whole label-selector API. In k8s.io/apimachinery, Requirement.Matches handles NotIn and != with one branch: if the label set does not have the key, return true. In and == take the opposite branch: if the label set does not have the key, return false. So env!=prod means "no env label saying prod", which a pod with no env label satisfies perfectly. If you meant "has an env label, and it is not prod", you need two clauses: env,env!=prod — Exists AND NotIn. This tester annotates every clause that holds because the key is absent, so you never have to remember which way round it goes.

matchLabels is a plain map, and every pair in it becomes one equality requirement: {app: web, tier: frontend} is exactly app=web,tier=frontend. matchExpressions is a list of {key, operator, values} entries and is the only way to write the set-based operators In, NotIn, Exists and DoesNotExist. When a selector has both, apimachinery ANDs everything together — LabelSelectorAsSelector adds each matchLabels pair and each expression to the same requirement list. There is no OR anywhere in a label selector, at any level.

No, and this is the mistake generated manifests make most often. A Service's spec.selector is typed map[string]string, not a LabelSelector: it takes a plain label map and supports equality only. Write matchLabels or matchExpressions there and the API server rejects the field outright. Deployments, StatefulSets, DaemonSets, Jobs and NetworkPolicy podSelector all take a real LabelSelector and accept both forms. Paste a Service manifest with matchExpressions into this tester and it warns you, then shows what a Deployment WOULD have matched — so you can see both the error and the intent.

It depends on whether the field is empty or absent, and the two are not the same. An empty LabelSelector — {}, or matchLabels: {} — is labels.Everything(): it matches every object, which is exactly why a NetworkPolicy with podSelector: {} applies to every pod in the namespace. A selector that is nil, on the other hand, matches nothing: LabelSelectorAsSelector(nil) returns labels.Nothing(). And a Service is a third case again: its selector field is omitted when empty, so a Service with selector: {} is a Service with no selector, and the endpoints controller does not manage its endpoints at all.

No. Both parsers, the validator and the matcher are JavaScript running in your tab — there is no server, no API call and no logging, so 0 bytes are uploaded. That matters here because a pod dump is not neutral text: it carries internal image references, node names, annotations, sometimes a token mounted as an env var. This is the tool you can use on production output without a conversation about data handling.

The pod labels, not the Service. Paste the Service manifest as the selector and the actual pods as the resources: the tester reads spec.selector for you and shows a per-clause trace on each pod, so a one-character label typo shows up as label tier="frontnd" ≠ "frontend" instead of as an empty endpoint list. Two structural traps to rule out as well. First, a Service selects POD labels, not the Deployment's own metadata.labels — this tester lists a workload's spec.template.metadata.labels as its own "Pod template" row for exactly that reason. Second, a selector that matches nothing is never an error anywhere in Kubernetes: kubectl accepts it, the API accepts it, and the only symptom is an empty endpoints object.

No, and that is a deliberate fence rather than a missing feature. Field selectors are a different mechanism: they filter on an object's own fields, they are evaluated by the API server against a small allow-list that varies per resource type, and almost none of them support set-based operators. Simulating them offline would mean encoding which fields each Kubernetes version indexes for each kind, and getting it subtly wrong. This tool covers label selectors, which are uniform across every resource type and fully defined by the object you already have in your clipboard.

They exist, but not on a label selector. Node affinity uses NodeSelectorRequirement, a separate type whose operator set is In, NotIn, Exists, DoesNotExist, Gt and Lt. A LabelSelectorRequirement — the one inside matchExpressions, spec.selector and podSelector — has only the first four. So Gt and Lt are valid in nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution and invalid everywhere this tester looks. Type replicas>2 into the selector field and it says so rather than inventing a comparison a label selector does not have.

It evaluates the first 500 resource documents from a paste of up to 500,000 characters, and a selector of at most 100 clauses — every cap says so on screen with the real count, so a truncated run never looks like a complete one. Keys and values are validated against the real limits: a label key name is at most 63 characters, an optional DNS-subdomain prefix before the / at most 253, and a label value at most 63 (an empty value is legal). An invalid key or value in the SELECTOR is a hard error, because the API server would reject it; an invalid label on a RESOURCE is only a note, because the question you asked was whether the selector matches it.

More free, private DevOps tools.

The Label Selector Tester is one tool in OpsCanopy — a growing canopy of browser-based validators, converters and testers that never touch a server.

39 free tools, every one offline-capable — opscanopy.com works with no signup and nothing uploaded.

Related: Kubernetes Resource Calculator for what a workload reserves, Env Example Checker for the variables a container expects, the Alertmanager Route Tester for the other label-matching tree that silently drops things, and the JSON ↔ YAML Converter when a manifest needs reshaping for kubectl patch — or browse the full tools directory.

Provided as-is for convenience; always confirm critical configuration against the cluster that will consume it. OpsCanopy is free and open.