skip to content

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%

answer

  1. same rule, three placements
  2. function call, loopback, network hop
  3. one process, one pod, everyone
  4. who ships the policy update
  5. who ends up holding the input

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.

solid answer

~50 s

OPA is a Go library first and a server second, so the same Rego rule — say, a check that a provisioning request names an approved region and cloud account — can be evaluated in three places. Embedded, you link OPA's Go packages and evaluate in-process: a function call, no network, and the input never leaves the process. As a sidecar, OPA runs as a second container in the pod and the service calls it over localhost, so load and failure are scoped to that one pod. As a shared decision service, one OPA deployment answers every caller over the network: one place to update policy, and one place that sees every input. Latency, blast radius, policy freshness and who sees the input all follow from that placement — not from the rule text, which is identical in all three.

go deeper

for a junior

Be ready to name the three placements and say, in one sentence each, where the decision is computed: inside your process, in a container next to it, or on a shared service across the network.

for a middle

Explain the mechanics behind each: what an in-process evaluation avoids, what a loopback HTTP call still costs in serialization, and how policy gets to an OPA that you did not rebuild.

for a senior

Show you would pick per service rather than per estate, and justify a mixed topology by naming the specific latency, isolation or data-sensitivity constraint that moved each service off the default.

for a principal

Own the consequence of standardizing: a single shared decision service creates a component your organization must staff and fund, while per-pod sidecars push that cost invisibly into every team's resource budget.

## The engine is a library that also ships as a server Open Policy Agent (OPA) is a general-purpose policy engine written in Go. Policies are written in Rego, and evaluation is a pure function: policy plus data plus an `input` document goes in, a result comes out. Because the engine is a Go package first and a daemon second, the *same* rule can be evaluated in three different places. Choosing among them is a deployment decision, not a policy decision — the Rego is byte-for-byte identical in all three. Take a concrete rule: a provisioning API must refuse to create anything outside an approved region or outside an approved cloud account. The rule reads `input.region` and `input.account_id` and consults a list of approved values. ### 1. Embedded as a Go library You import OPA's Go packages — the `rego` package to prepare and evaluate a query directly, or the `sdk` package for a managed embedded OPA instance — and evaluate inside the provisioning service itself. The decision is a function call. There is no socket, no JSON round trip to another process, nothing that can be unreachable independently of the caller. The input document is only ever a value in the caller's own memory. The constraint is that this is Go. A Python or Java service cannot link OPA's Go packages; it has to reach OPA some other way. (Rego can be compiled to a Wasm module and evaluated from other languages, but that is a different, more limited execution path, not the same as embedding the engine.) ### 2. A per-pod sidecar OPA runs as a second container inside the same Kubernetes pod as the service, and the service calls it over `localhost`. The call is now a real HTTP request with real serialization, but it never leaves the pod: no cluster network, no shared capacity, no other tenant's traffic. If a sidecar wedges, exactly one pod is affected. Language no longer matters — anything that can make an HTTP call can ask for a decision. The price is multiplicity: every replica of every service that wants decisions carries its own OPA process, its own copy of the policy and of any base data the policy consults, and its own connection to wherever policy comes from. ### 3. One shared decision service One deployment of OPA in server mode answers every caller over the network. You get a single place to update policy, a single place to observe decisions, and a single component to operate and upgrade. You also get a component in everyone's request path, a network hop on every decision, capacity you must size against aggregate load, and — the axis candidates most often miss — a single process that receives every caller's raw input document. ### The four axes to compare on | Axis | Embedded library | Sidecar | Shared server | |---|---|---|---| | Latency | In-process call; no network | Loopback call inside the pod | Network hop plus queueing at a shared component | | Blast radius | Scoped to that one process | Scoped to that one pod | Every caller at once | | Policy freshness | If policy is built into the binary, a new rule needs a rebuild and a release | Fetched at runtime by each sidecar | Fetched at runtime once, by one component | | Who sees the input | Nobody — it never leaves the process | The pod, plus wherever its decision logs go | The service operator, plus wherever its decision logs go | ### How the choice actually gets made In practice the language of the caller decides first: non-Go services cannot embed, which removes the option before any trade-off is weighed. Then data sensitivity: if the input carries tenant or customer identifiers that must not cross a boundary, embedding or a sidecar keeps it local. Then operations: a fleet of thousands of pods makes the per-pod cost of a sidecar real, while a handful of high-value services makes it cheap. Most mature estates end up mixed — sidecars or embedding for the few services with strict latency or data constraints, one shared decision service for the long tail — and that is a perfectly good answer to give, provided you can say *why* each service landed where it did. What does not change with placement is correctness. The same input against the same policy and data yields the same result everywhere; centralizing does not make a rule more right, and embedding does not make it weaker. Placement buys you latency, isolation, freshness and confidentiality characteristics — nothing else.

  • A Python service needs the same rule. Which placements are still open to it?
    The sidecar and the shared decision service — both are reached over HTTP, so any language can call them. Embedding is OPA's Go packages linked into a Go binary, so it is off the table. Compiling the Rego to a Wasm module and evaluating it from Python is a separate, more limited path, not the same as embedding the engine.
  • If the Rego is identical in all three, what actually differs in what you ship?
    The unit of delivery. Embedded, the policy is part of the service artifact and moves through its release. As a sidecar or a server, the policy is a bundle fetched at runtime by the OPA process, so the service artifact does not change when a rule changes. That difference decides whether a policy update is a code release or a configuration event.
  • Why is the sidecar usually described as the middle ground?
    It gets the language independence and runtime policy updates of the server while keeping the call and the input inside the pod, so a failure is scoped to one workload. What it does not get is the server's single-copy economics: you pay its memory and its policy fetch once per replica, across the whole fleet.

It is the difference between calling a function you compiled in, asking the colleague sitting at the next desk, and phoning one central help desk that the whole company shares.

saying these in an interview costs you the question

  • Thinks the sidecar is a shared cluster-wide service
  • Believes an embedded library still calls OPA over HTTP
  • Treats the three placements as differing only in latency
  • Claims centralizing the engine makes the rule more correct
  • Assumes any language can link OPA's Go packages

context