Relations, not single resources
Finds what a per-resource check cannot see. KubePattern rebuilds a graph of your live cluster and looks at how resources refer to each other.
Every manifest is valid on its own. The defect shows up only in the relation: an unused definition, a dangling reference, a missing companion. KubePattern finds them across any Kubernetes CRD, with patterns you write as YAML.
How it works
KubePattern is a Go analyzer that runs inside your cluster. Each scan reads your patterns, fetches what they need, and writes back one Smell per defect it finds.
Patterns are custom resources in your cluster. A linter checks each one first: a malformed pattern is logged and skipped, never run.
Kinds are resolved at run time through API discovery, so custom resources unknown at build time work. Only the kinds a pattern names are listed.
Resources become the vertices of a graph, built once per run. Each relationship in a pattern is a query over it.
Every target that matches becomes a Smell, with a stable identity and a first observation. Fixed smells disappear on the next run.
Pattern-as-Code
A pattern describes the defect, not the rule. Name the resource it is about, the resources that give it context, and the relation that makes it a smell.
matchNone: no instance of the dependency relates to the target.apiVersion: kubepattern.dev/v1
kind: Pattern
metadata:
name: cnpg-cluster-without-scheduledbackup
spec:
displayName: PostgreSQL Cluster Without Scheduled Backup
category: Reliability
severity: HIGH
for: 10m
message: "Cluster {{target.metadata.name}} has no ScheduledBackup."
target:
kind: Cluster
apiVersion: postgresql.cnpg.io/v1
plural: clusters
dependencies:
- id: schedule
kind: ScheduledBackup
apiVersion: postgresql.cnpg.io/v1
plural: scheduledbackups
relationships:
matchNone:
- with: schedule
type: custom
criteria:
- targetPath: "metadata.name"
dependencyPath: "spec.cluster.name"
operator: EQUALS
- targetPath: "metadata.namespace"
dependencyPath: "metadata.namespace"
operator: EQUALSapiVersion: kubepattern.dev/v1
kind: Smell
metadata:
name: cnpg-cluster-without-scheduledbackup-9c1e4d2a-5b7f-4c1e-8a3d-2f6b0e91c7a4
namespace: orders
labels:
kubepattern.dev/phase: Pending
spec:
name: cnpg-cluster-without-scheduledbackup
category: Reliability
severity: HIGH
phase: Pending
since: "2026-10-11T09:00:04Z"
suppress: false
message: "Cluster pg-orders has no ScheduledBackup."
target:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
name: pg-orders
namespace: orders
uid: 9c1e4d2a-5b7f-4c1e-8a3d-2f6b0e91c7a4What it finds
Each family is a relation that is missing, or one that should not be there. The same engine finds them in GitOps tools, operators, control planes and platform portals, with no code changes.
ClusterIssuer that no Certificate uses.Composition for a kind that no XRD defines.Cluster with no ScheduledBackup.Pooler named like a Cluster in the same namespace.Built for GitOps timing
A GitOps tool that re-creates a referrer makes a healthy resource look unused for a few minutes. Give a pattern a minimum duration and the finding stays Pending until the condition has persisted.
spec.since and keeps its identity across runs.Hourly CronJob · pattern with for: 90m
A GitOps tool deletes and re-creates the only referrer of a resource. For a few minutes the resource looks unused.
| Row | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 |
|---|---|---|---|---|---|
| Condition holds | No | Yes | No | No | No |
| Smell | No smell | Pending | Deleted | No smell | No smell |
Never becomes Active. Nobody is paged for a blip.
Nothing refers to the resource, and nothing is going to.
| Row | Run 1 | Run 2 | Run 3 | Run 4 | Run 5 |
|---|---|---|---|---|---|
| Condition holds | No | Yes | Yes | Yes | Yes |
| Smell | No smell | Pending | Pending | Active | Active |
Becomes Active at run 4, the first run at least 90 minutes after the first observation.
Install the chart, apply a pattern, and read the results with kubectl get smells -A.
helm upgrade --install kubepattern \
oci://ghcr.io/kubepattern/charts/kubepattern \
--version <VERSION> --namespace kubepattern-system --create-namespace