skip to content

OPA & Gatekeeper

OPA runs as a library, a sidecar or a shared server fed by bundles, while Gatekeeper wraps Rego in ConstraintTemplates with audit and mutation. Interviewers probe topology and decision logs.

on this pageshow

explore

questions

page 1 of 2

What does an OPA bundle contain, and what is the revision in its .manifest for?

level: juniorimportance: must knowfreq 66%

answer

  1. gzipped tarball, pulled not pushed
  2. rego plus data plus one metadata file
  3. manifest holds revision and roots
  4. opaque label, never compared by OPA
  5. echoed in status and decision logs

basics

~20 s

An OPA bundle is a gzipped tar archive of Rego policy, data files and an optional .manifest. The manifest's revision is an opaque label for that build, reported in OPA's status and decision output so you know which policy decided.

solid answer

~50 s

A bundle is how a running OPA gets its policy: the bundle plugin polls an HTTP bundle service on an interval, downloads a gzipped tarball and activates it into the process. Inside the tarball are `.rego` policy files, `data.json`/`data.yaml` files whose position in the archive decides where they land under `data`, and an optional `.manifest` at the archive root. The manifest carries `revision` (an opaque string the publisher chooses), `roots` (the slice of the policy and data namespace this bundle owns) and optional `metadata`. OPA never parses or orders revisions — it activates whatever it just downloaded — so the revision exists for correlation, not for version comparison. It shows up in the status plugin's report and in decision log entries, which is what lets you answer "which policy admitted this request?". Most teams set it to the git commit that built the bundle.

go deeper

for a junior

Be ready to say what an OPA bundle physically is (a gzipped tarball of Rego and data plus a .manifest) and that the revision is an opaque build label, not a version OPA compares.

for a middle

Explain the mechanics: the plugin polls a service, activation replaces content under the bundle's roots, and the revision is echoed in the status report and in decision log entries.

for a senior

Show you use the revision operationally — pin it to a git SHA so a decision record resolves to a commit, and treat the status report's active revision as the ground truth for what is enforcing.

for a principal

Own the convention across teams: what a revision string must contain so an audit question becomes a lookup, and who is accountable when the label and the deployed rules diverge.

## Where policy actually comes from A long-running OPA does not read policy from a git repository, a mounted volume or a ConfigMap at request time. In the bundle model, OPA's bundle plugin is configured with a service URL and a resource path, polls that service on an interval, downloads a **bundle** and **activates** it into the running process. From then on every query is answered out of the in-memory policy and data that activation installed. That single fact drives most of what interviewers ask here: the thing serving decisions is a snapshot the agent pulled at some point in the past, and the interesting questions are about how you know *which* snapshot. ## What is in the tarball A bundle is a gzipped tar archive. It contains: - **`.rego` files** — the policy. Their package declarations, not their file paths, decide where the rules live under `data`. - **data files** — `data.json` or `data.yaml`. Here the *path in the archive* matters: a file at `k8s/registry/data.json` lands at `data.k8s.registry`. - **`.manifest`** — optional, at the root of the archive, and JSON: ```json { "revision": "7f3a1c9-2026-08-14", "roots": ["k8s/deprecated", "k8s/registry"], "metadata": {"built_by": "policy-ci"} } ``` - **`roots`** declares the slice of the document tree this bundle owns. Every policy package and every data path the bundle ships must fall inside one of its roots, and on activation OPA erases what is currently under those roots before writing the new content. If `roots` is omitted it defaults to the empty root `""`, meaning the bundle owns the entire tree. - **`metadata`** is a free-form object OPA carries along without interpreting. ## The revision string `revision` is **opaque**. OPA does not parse it, does not compare it to the one it already has, and has no notion of a newer or older revision. It never refuses a bundle because the revision looks like it went backwards, and it never activates a bundle because the revision looks newer. Activation is driven purely by "the service handed me a new body" — the revision is a label riding along with it. That label is valuable precisely because it is carried through to the outputs: - The **status plugin** reports, per configured bundle, the currently active revision plus the timestamps of the last successful download and activation and the last error. - **Decision log** entries carry the bundle name and the revision that was active when that decision was made. So when someone asks "was the deprecated-API ban actually in force when this Ingress was admitted at 14:02?", the answer is a revision string in a decision record, and that string maps back to a build. This is why the common convention is to set the revision to the git SHA of the policy repository, often with a build timestamp appended: it turns an operational question into a `git show`. What the revision is **not**: it is not a version constraint, not an ordering, and not a statement about who produced the bundle or whether it was tampered with. It is a name. ## Sibling bundle shapes Two variants use the same plumbing: - A **delta bundle** ships only changes to `data` rather than a whole snapshot, so a big data document can be kept fresh cheaply. It carries a patch, not a full tree, and it cannot change policy. - A **discovery bundle** is a bundle whose payload is OPA's *own configuration* — which bundles to fetch, where to send decision logs and status. OPA fetches it first and configures itself from it, which is how a fleet of agents gets reconfigured centrally without a redeploy. It has a revision of its own, exactly like a policy bundle. ## Reading a bundle in an interview If you are handed a manifest, the three things to say out loud are: the revision tells me which build this is and I expect to see it echoed in decision logs; the roots tell me what this bundle is allowed to own and what it will wipe on activation; and neither field makes OPA reject a bundle for being stale, because staleness is something you have to detect yourself from the status report.

  • If OPA never compares revisions, what stops it activating an older bundle the service accidentally republishes?
    Nothing in OPA. The bundle plugin activates whatever body the service returns, whatever its revision says. Preventing a rollback to last month's policy is entirely the publishing side's job — the service decides what to serve. On the agent side all you get is visibility: the status report and decision logs will show the older revision suddenly active, which is why alerting on an unexpected revision is worth the effort.
  • What would you put in the revision string, and why?
    The git commit SHA of the policy repository that produced the bundle, usually with a build timestamp or pipeline run id. It makes the revision in a decision log directly resolvable: given a decision that allowed something it should not have, you can check out that exact commit and read the rule that ran. A hand-incremented number or a date alone loses that link.
  • What is a discovery bundle, and how does it differ from the bundle carrying your rules?
    Same tarball format, different payload: a discovery bundle carries OPA's own configuration — which bundles to download, decision log and status sinks — rather than admission rules. OPA fetches it first and configures its other plugins from it, so a fleet can be repointed or have logging turned on centrally without redeploying the agents. It has its own revision, reported separately.

The revision is a shipping label on a crate, not a version number in a package manager: it tells you which crate arrived, but nothing about it makes the warehouse refuse an older one.

saying these in an interview costs you the question

  • Says OPA compares revisions and rejects older ones
  • Thinks the revision is a checksum OPA verifies
  • Believes OPA reads policy from git at request time
  • Cannot say where the revision is visible at runtime
  • Confuses roots with filesystem paths in the archive

context

open as a page

What is OPA's data document, and what are the ways facts get loaded into it?

level: juniorimportance: must knowfreq 72%

basics

~20 s

OPA's data document is the in-memory JSON tree of standing facts policies read as data.something, separate from the input being decided. Facts arrive as startup files, inside a bundle OPA polls for, or as pushes to its Data API.

open as a page

What does Gatekeeper's audit controller do, and where does it write what it finds?

level: juniorimportance: must knowfreq 66%

basics

~20 s

Gatekeeper's audit controller periodically re-evaluates objects that already exist in the cluster against every enforced Constraint and records the violations in each Constraint object's own status field. It only reports: it never blocks, deletes or changes anything.

open as a page

What does Gatekeeper's gator test command evaluate, and what must you feed it?

level: juniorimportance: must knowfreq 60%

basics

~20 s

gator test evaluates Gatekeeper policy locally: you hand it ConstraintTemplates, their Constraints, and the Kubernetes objects to review, and it reports which object violated which constraint. It needs no cluster, no kubeconfig and no admission webhook.

open as a page

What does a Gatekeeper Assign resource do, and how does it differ from a Constraint?

level: juniorimportance: must knowfreq 64%

basics

~10 s

A Gatekeeper Assign is a mutator: at admission it writes a value into a field of the incoming object, so the object is stored changed instead of rejected. A Constraint only inspects and denies.

open as a page

In a Gatekeeper ConstraintTemplate, what must the Rego produce to deny a request?

level: juniorimportance: must knowfreq 76%

basics

~20 s

Gatekeeper denies a request when the template's violation rule produces a non-empty set of objects, each carrying a msg string. An empty set means the constraint is satisfied. Each object may also carry an optional details object.

open as a page

In Gatekeeper, what do the Config and SyncSet objects do, and where does the synced data appear to a rule?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Gatekeeper's Config (spec.sync.syncOnly) and SyncSet objects choose which resource kinds are replicated into its in-memory cache. Those objects then appear to Rego under data.inventory, so a constraint can read cluster objects other than the one being admitted.

open as a page

In OPA Gatekeeper, what is the difference between a ConstraintTemplate and a Constraint?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A 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.

open as a page

What does a POST to OPA's /v1/data endpoint send and return?

level: juniorimportance: must knowfreq 74%

basics

~20 s

You POST to /v1/data followed by the document path, with a body of the form {"input": {...}} carrying the facts. OPA evaluates that document and replies 200 with {"result": <value>} - the value the rule produced.

open as a page

OPA can run as an embedded Go library, a per-pod sidecar or a shared server — what changes between them?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Where the decision is computed. Embedded is an in-process function call with no network. A sidecar keeps the call on the pod's loopback. A shared server puts one OPA in every caller's path over the network.

open as a page

In an OPA Envoy ext_authz policy, where in input do the request method, path and body live?

level: juniorimportance: must knowfreq 72%

basics

~10 s

Under input.attributes.request.http: method, path, headers with lowercased keys, and body as a string. The OPA Envoy plugin also adds three top-level conveniences: input.parsed_path, input.parsed_query and input.parsed_body.

open as a page

How do you keep a secret out of OPA's decision log when the policy input contains it?

level: middleimportance: must knowfreq 64%

basics

~20 s

Write a mask policy, by default at data.system.log.mask, returning JSON pointers into the log event such as /input/... OPA erases or replaces those fields before the entry leaves the process, and records which paths it erased.

open as a page

When Gatekeeper evaluates a violation rule, what is in input and what is not?

level: middleimportance: must knowfreq 67%

basics

~20 s

Two things only: input.review, holding the resource under decision as input.review.object (and its previous version as oldObject on an update), and input.parameters, holding the matched Constraint's parameters. No other object, no cluster state, no history.

open as a page

Your OPA gate reports healthy but is still enforcing last quarter's rules — how do you diagnose it?

level: seniorimportance: must knowfreq 52%

basics

~20 s

A failed bundle download or activation leaves the last activated bundle deciding, so OPA answers confidently on old policy. Compare the active revision in status and decision logs with the revision the service intended to serve.

open as a page

In OPA, what does a single decision log entry record about a policy decision?

level: juniorimportance: should knowfreq 55%

basics

~10 s

Each OPA decision log entry is a JSON record of one policy evaluation: a unique decision_id, the queried policy path, the input document, the result returned, the bundle revision in force, and a timestamp.

open as a page

How does OPA's bundle plugin avoid re-downloading and re-activating an unchanged bundle?

level: middleimportance: should knowfreq 50%

basics

~20 s

OPA replays the Etag from its last successful bundle download as If-None-Match on the next poll. If the service answers 304 Not Modified, OPA skips the download and the activation and keeps serving the bundle it has.

open as a page

On OPA's Data API, how do PUT and PATCH differ when writing facts?

level: middleimportance: should knowfreq 52%

basics

~10 s

PUT replaces the entire document at the given path, so anything previously under it is gone. PATCH takes a JSON Patch operation list and edits in place, changing only the paths those operations name.

open as a page

In Gatekeeper's gator verify, what can a Suite case assert beyond a plain denial?

level: middleimportance: should knowfreq 46%

basics

~20 s

A gator verify case can assert how many violations the object produced and what the violation message said, not merely that something was denied. That pins which rule fired and what a developer will be told when it does.

open as a page

Your Gatekeeper mutator must add ALL to every container's dropped capabilities — Assign or ModifySet?

level: middleimportance: should knowfreq 50%

basics

~20 s

ModifySet. Assign writes the whole value at the location, so it would replace whatever drop list the author already had. ModifySet treats the list as a set and merges the new member in, leaving the existing entries alone.

open as a page

How does a Gatekeeper rule read a sibling object from data.inventory, and what happens if that kind was never synced?

level: middleimportance: should knowfreq 48%

basics

~10 s

A rule indexes data.inventory by scope, groupVersion, kind and name. An unsynced kind makes that lookup undefined, not false — so the surrounding logic decides whether the rule blocks everything or waves everything through.

open as a page

What do Gatekeeper's enforcementAction values deny, dryrun and warn each do to a request?

level: middleimportance: should knowfreq 61%

basics

~20 s

deny rejects the request and returns the violation message. dryrun admits it and only records the violation on the Constraint's status. warn admits it too, but the API server returns a warning that the person applying sees immediately.

open as a page

Why does OPA's /v1/data return 200 with an empty JSON body?

level: middleimportance: should knowfreq 52%

basics

~20 s

Because the document you queried is undefined: nothing produced a value, so there is no result field to return. Evaluation itself succeeded, which is why the status is still 200. An empty body is no decision - neither an allow nor a deny.

open as a page

OPA is embedded in ten Go services with the policy baked into each binary — how does a new rule roll out?

level: middleimportance: should knowfreq 44%

basics

~20 s

One rebuild and release per service. With Rego compiled into the binary there is nothing to push, so enforcement coverage follows ten release trains, and for a while some services enforce the new rule and some the old one.

open as a page

An OPA ext_authz rule matched a URL path exactly; adding ?page=2 now returns 403. Why?

level: middleimportance: should knowfreq 55%

basics

~10 s

Because input.attributes.request.http.path is the raw request target and still carries the query string, so the equality test against "/api/v1/orders" fails. Match input.parsed_path instead, which is query-free and percent-decoded.

open as a page

Your OPA gate denies an instance type approved this morning. How do you diagnose and unblock it?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Read what the running engine holds, not what the source of truth says: GET the approved list from that OPA and compare. A correct rule denying a newly approved value is a data-freshness failure, fixed on the data path.

open as a page

A Gatekeeper Constraint's status lists 20 violating PVCs; why is that not the offender count?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Because the violations list in a Constraint's status is truncated, not complete: Gatekeeper stores at most --constraint-violations-limit entries per Constraint, 20 by default. The real count sits beside it in status.totalViolations, as of the last audit timestamp.

open as a page

A Gatekeeper constraint passes its gator suite but never fires on Deployments — how do you catch that offline?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Use gator expand with an ExpansionTemplate: it renders the Pod a Deployment, Job or CronJob would produce, and gator test and verify then review that generated Pod. The false green is a Pod rule tested only on bare Pods.

open as a page

Your Gatekeeper Assign is installed but the Pods come out unchanged — how do you debug it?

level: seniorimportance: should knowfreq 43%

basics

~20 s

A mutator that matches nothing fails silently, so work outward: confirm which object is actually admitted, then applyTo's group/version/kind, then the match block, then the location path and pathTests — and verify on a server-side dry-run of a real object.

open as a page

Why can't a Gatekeeper ConstraintTemplate call http.send, and what replaces it?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Gatekeeper evaluates template Rego without http.send. A call during admission would put an outside service on every matching write's path and make results non-reproducible. Facts are pushed in instead: parameters, replicated objects, or an external data provider.

open as a page

A Gatekeeper constraint blocked a Deployment for a missing NetworkPolicy applied seconds earlier — why, and what do you do?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Replication lag. A Gatekeeper rule reads data.inventory, a watch-fed replica rather than a live query, so the NetworkPolicy existed in the API server but had not reached the cache when the decision was made.

open as a page

showing 1–30 of 45