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