In OPA Gatekeeper, what is the difference between a ConstraintTemplate and a Constraint?
answer
- two objects, not one
- one defines, the other switches on
- applying the template generates a CRD
- kind name comes from the template
- no instance means no enforcement
basics
~20 sA ConstraintTemplate defines a reusable rule plus a schema for its parameters, and makes Gatekeeper generate a new custom resource kind. A Constraint is an instance of that kind: it switches the rule on, scoped and parameterised.
solid answer
~40 sGatekeeper stores policy as two Kubernetes objects. The **ConstraintTemplate** carries the rule logic, the name of the new kind it will create (`spec.crd.spec.names.kind`, say `K8sRequiredResources`), and an OpenAPI v3 schema for the parameters that kind accepts. Applying it makes Gatekeeper generate a cluster-scoped CustomResourceDefinition in the `constraints.gatekeeper.sh` group — and nothing else; no request is affected yet. A **Constraint** is an object of that generated kind. It carries `spec.match` (which kinds and namespaces the rule applies to), `spec.parameters` (validated against the template's schema), and `spec.enforcementAction`. One template can back many Constraints with different scopes and different parameter values, which is the whole point: a platform team ships and tests the template once, and it is instantiated per team or per tier rather than copy-pasted.
go deeper
Be ready to name both objects and say which one is actually in force. Remember that applying only a template changes nothing, and that the Constraint is where the match block and parameter values live.
Explain the mechanics: the template's crd.spec.names.kind and OpenAPI v3 parameter schema cause Gatekeeper to generate a cluster-scoped CRD, and Constraints are ordinary custom resources of that kind, validated by that schema at apply time.
Show you use the split deliberately: one reviewed template backing several scoped Constraints, and the operational awareness that deleting a template garbage-collects every Constraint built on it.
Own the ownership boundary this split creates — who is allowed to ship templates, who may create Constraints, and how you keep a shared template library from forking into one near-copy per team.
## Policy as two objects OPA Gatekeeper does not keep rules in a config file that the engine reads. It keeps them as Kubernetes objects, and there are two distinct ones. Interviewers ask this because candidates who have only run `kubectl apply -f` on someone else's policy bundle usually cannot say which object does what. ### The ConstraintTemplate — the reusable rule A ConstraintTemplate lives in the `templates.gatekeeper.sh` API group and holds three things: - **The name of the kind it will create.** `spec.crd.spec.names.kind` — for example `K8sRequiredResources`. This is the vocabulary word you are teaching the cluster. - **A parameter schema.** `spec.crd.spec.validation.openAPIV3Schema` describes what `spec.parameters` may contain on instances of that kind: which properties exist, their types, which are required. - **The rule logic**, under `spec.targets`, evaluated against the admission request. Applying a ConstraintTemplate has exactly one visible effect: Gatekeeper's controller generates a CustomResourceDefinition for that kind in the `constraints.gatekeeper.sh` group, cluster-scoped. Nothing is enforced. You have added a noun to the cluster's API, not a guardrail. ### The Constraint — the instance that is actually in force A Constraint is an object of that generated kind. Its shape is fixed by Gatekeeper plus your schema: - `spec.match` — which resources this instance applies to: kinds, namespaces, excluded namespaces, label selectors. - `spec.parameters` — the values, validated by the generated CRD's schema at `kubectl apply` time. - `spec.enforcementAction` — `deny` by default; `dryrun` and `warn` are the other values. Constraints are cluster-scoped objects even when the resources they match are namespaced. Their `status` is where Gatekeeper reports what its periodic audit found on objects already living in the cluster. ### Why the split is worth the extra object Because one template can back many Constraints. Take a rule that every container must declare CPU and memory requests and limits, with the ceilings as parameters. Written as one template, you can then create a strict Constraint for production namespaces with low ceilings, a looser one for a batch namespace, and a third scoped to a single team's namespace while they migrate. The Rego is written, reviewed and tested once; only the small, readable Constraint objects multiply. That also splits ownership cleanly. A platform or security team owns the template — the logic, its tests, its rollout. Application or namespace owners own the Constraints, which are short YAML documents with a match block and a few values, reviewable by someone who does not read policy code. If instead you fork the template for every variation, you get N copies of the same rule that drift apart, N generated CRDs, and N test suites. ### The traps **"I applied the template and nothing is blocked."** Expected: a template with no Constraint enforces nothing. This is the single most common first-day confusion, and it is also a safety property — you can install a template library on a cluster without changing any request's outcome until someone creates an instance. **Deleting the template deletes everything built on it.** Removing a ConstraintTemplate removes the generated CRD, and with it every Constraint of that kind. Enforcement disappears cluster-wide and silently, so a template is not something to `kubectl delete` casually to "unblock a team" — the narrower fix is to change the offending Constraint. **Parameters do not live on the template.** The template declares their *schema*; the values live on each Constraint. Likewise `match` and `enforcementAction` are per-Constraint, so two instances of one template can differ in both scope and severity. **A bad parameter is caught by the API server, not by the rule.** Because the generated CRD carries the schema you wrote, a Constraint that sets an integer field to a string is rejected at apply time with a validation error. That is the payoff for writing a real schema instead of an empty one: the failure lands on the person editing the object, in the moment, instead of surfacing later as a rule that quietly matches nothing. ### How to say it in an interview "Template defines and generates; Constraint instantiates and scopes. The template gives you a new CRD; the constraint is the object of that kind that is actually in force, with its own match block, parameters and enforcement action."
- You applied the ConstraintTemplate and nothing is being rejected. Why?Because a template on its own enforces nothing. It registers the rule and makes Gatekeeper generate the CustomResourceDefinition for its kind, but no request is evaluated until you create a Constraint of that kind with a match block. This is deliberate: you can install a whole template library without changing any outcome.
- How many Constraints can one ConstraintTemplate back, and why does that matter?As many as you like. Each carries its own match block, parameters and enforcementAction, so one reviewed and tested rule can run with tight ceilings in production namespaces and looser ones elsewhere. That is what stops the template being copy-pasted into near-identical forks that drift.
- What happens to existing Constraints if you delete their ConstraintTemplate?They go with it. Deleting the template removes the generated CustomResourceDefinition, and Kubernetes garbage-collects every custom resource of that kind, so all instances of the rule stop being enforced cluster-wide. If one Constraint is causing trouble, edit or delete that Constraint, not the template.
saying these in an interview costs you the question
- Says the ConstraintTemplate itself blocks requests
- Thinks parameter values live on the template
- Cannot say where match and enforcementAction live
- Writes a new template for every threshold change
- Believes deleting a template leaves its Constraints working