CLI tool
open-policy-agent/opa avatar
open-policy-agent/opa

OPA and Rego: policy as a separate decision service

Open Policy Agent (OPA) is an open source, general-purpose policy engine.

12,341 stars1,691 forksGoApache-2.0

At a glance

What is it?
Open Policy Agent evaluates authorization and compliance decisions in its own language, so services ask a question over a query API instead of hardcoding the answer.
Who is it for?
OPA is worth the extra hop when the same question gets asked in several places, which is the usual case for anything spanning an API gateway, Kubernetes admission and CI. It is the wrong shape when there is exactly one caller and one rule, because a policy in Go and a policy in Rego cost roughly the same to write and only one of them needs a network hop per request.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Rules in, decisions out, one query at a time

The mechanism is small enough to state in a sentence and specific enough to matter. You write rules in Rego. You integrate a service with OPA by executing queries against it. When a query arrives, OPA evaluates the rules against the data you gave it and returns a decision. Your service enforces that decision, it does not compute it.

The README names the questions those rules are supposed to answer, and they are the right three to design around: can user X call operation Y on resource Z, what clusters should workload W be deployed to, what tags must be set on resource R before it is created. Each is a decision with an input and an output, and each would otherwise be a conditional sitting in the middle of a handler.

The API authorization case is spelled out step by step in the README. You write rules that allow or deny access to your service APIs, the service queries OPA when it receives a request, OPA returns allow or deny, and the service accepts or rejects accordingly. The important word in that sequence is queries. OPA does not push, and it does not hold your sessions, so the availability of your authorization decision is a property of the path between your service and OPA rather than of OPA alone.

Three integration doors: Go SDK, low-level API, REST

The README separates integration by the language of the calling service, and the split is worth reading carefully.

For Go services there are two options, described as a high level Go SDK and the low level Go API. The low level path is the `rego` package, which is the piece a library user would reach for when the SDK does not fit. Either way, an in-process call is possible, which removes the network hop entirely at the cost of a Go dependency on the OPA module.

For everything else there is the REST API, with a reference page for it. That is the door for a service written in Python, TypeScript, Java or anything else, and it is also the door for a language-agnostic enforcement point such as an Envoy sidecar.

The integration docs list more, and the README names the systems it is used with: Kubernetes, Terraform, Docker authorization, and SSH and sudo authorization. That last one is the most telling, because sudo authorization is the least glamorous case there is and it is still worth writing in one policy language rather than in three sudoers files.

The topic list on the repository says the same thing from the other side: authorization, cloud-native, compliance, declarative, policy.

Getting the binary and running it as a container

The README's own pointer for running OPA is the documentation section called Running OPA, and for artifacts it points at Docker Hub for container images and the GitHub releases page for binaries. No install command appears in the README itself.

The repository's Dockerfile is short and tells you the shape of the container anyway. It takes a base image as a build argument, drops the prebuilt binary in as `/opa`, appends its directory to `PATH`, and defaults the command to `run`:

dockerfile
ARG BASE
FROM ${BASE}
COPY ${BIN_DIR}/opa_${TARGETOS}_${TARGETARCH}${BIN_SUFFIX} /opa
ENTRYPOINT ["/opa"]
CMD ["run"]

Two details in that file are deliberate. The process runs as `1000:1000` rather than a named user, and the comment explains why: a named user will not work because Kubernetes pod security contexts running as non root cannot resolve the user ID. The second detail is the default command being `run`, which means a container with no arguments starts a server.

The module path is `github.com/open-policy-agent/opa` and it declares Go 1.26.0, so building from source needs a current toolchain. The dependency list in `go.mod` also tells you what the binary carries: Prometheus client libraries for metrics, OpenTelemetry for traces and metrics, cobra and viper for the command line and configuration, wazero for WebAssembly, jwx for JSON web tokens, and badger as an embedded store.

Reading the directory list as an architecture map

The repository tree is unusually legible as a description of how the engine is put together, and it is worth reading before you adopt the thing.

`ast/` and `compile/` are the front of the pipeline, parsing and compiling policies. `ir/` is the intermediate representation between them. The build directory, `builtin_metadata.json` and `capabilities.json` describe what the engine knows how to do and what a given target can support, which is the machinery behind building a policy for a constrained runtime.

Bundling and distribution have their own directories: `bundle/`, `download/`, `plugins/`, `loader/` and `keys/`. Bundles are how policy actually reaches a running OPA, signing keys are how you know a bundle came from where you think, and the plugin system is how OPA picks up changes without a restart. That trio is the part of OPA you will think about most in production and it is not mentioned in the README at all.

The rest of the list is engineering surface: `cover/` for coverage of policies, alongside `debug/`, `format/`, `metrics/`, `profiler/`, `features/`, `e2e/`, `refactor/`, `dependencies/`, plus `docs/`, `CHANGELOG.md`, `MAINTAINERS.md`, `GOVERNANCE.md`, `SECURITY.md` and a `SECURITY_AUDIT.pdf` at the root. A project that ships a published security audit alongside a governance document is telling you how it is run.

What the README settles and what it hands to the website

Read this README as an index. It answers what OPA is, where policy decisions sit in a request, which integration doors exist, where artifacts are published, and how to get help. It links out for everything else: the Rego language reference, deployment and integration guides, the REST API reference, the editor support page, the ecosystem directory, the learning resources and the roadmap.

It also contains no policy examples. Not one Rego snippet appears in the file, which is unusual for a language project and means the language is learned elsewhere. The paths it offers for that are the Rego Playground in a browser, a set of access control examples inside it, and a VS Code extension that gives live diagnostics, debugging and formatting. So the first session with OPA is realistically: write a rule in the playground, install the editor extension, run the CLI locally, then read the docs.

Support is arranged in three tiers, which tells you something about the project's posture. There is a Slack workspace with a help channel, community discussions on GitHub, and a support page listing commercial options. The last of those is how a CNCF project tells you that the free path is complete but not free for everyone.

Where a policy service is the wrong shape

OPA is strongest when one question has many callers, and weakest when it has one. A service with a single endpoint and a single rule does not get value from moving that rule into a network service, and it gains a new failure mode: the request now depends on OPA being up. The README does not address latency, availability budgets or what a service should do when a policy query fails. Those are integration questions, and they belong to the integration documentation, not to a decision engine that has no opinion about your traffic.

Second, Rego is a language to learn and a language to version. Policies are code that gets reviewed, tested and deployed, and the repository's `cover/`, `format/` and `format`-adjacent tooling reflects that OPA expects policies to be treated as source. Teams that already have a policy system in their application framework should ask what OPA buys them beyond a shared language.

Third, adoption evidence is easy to find and easy to over-read. The README points at `ADOPTERS.md` as a list of production adopters and invites organisations to add themselves by pull request, which is a useful honest signal but not a curated one. The project is a CNCF graduate, which is a stronger statement about governance than about fit.

None of that makes OPA a bad choice. It makes it a choice that pays off in proportion to how much policy you have scattered across services.

Editorial conclusion

OPA is worth the extra hop when the same question gets asked in several places, which is the usual case for anything spanning an API gateway, Kubernetes admission and CI. It is the wrong shape when there is exactly one caller and one rule, because a policy in Go and a policy in Rego cost roughly the same to write and only one of them needs a network hop per request. Before you commit, read the Rego documentation rather than the README, since the README has no policy examples in it at all, and check the integration docs for the language your service is written in. Then look at `ADOPTERS.md` to see whether organisations in your position are already running it, and start with the local `opa run` loop and the Rego Playground before wiring anything to a service.

Frequently asked questions

Is Open Policy Agent free?

Yes, the licence is Apache-2.0 and the repository carries a LICENSE file with that licence. The project also lists commercial support options on its support page, and offers a Slack workspace and community discussions for questions at no cost.

How do I run a policy decision locally before deploying OPA?

The README points at the Running OPA instructions for the local command line, and the container image defaults to the `run` command, which starts a server. For learning rather than running, it recommends the Rego Playground in a browser and a VS Code extension that provides live diagnostics, debugging and formatting.

How do I call OPA from a service that is not written in Go?

The README directs other languages to the REST API, with a reference page for it. The Go paths are separate: a high level Go SDK and a low level Go API built on the `rego` package, both linked from the integration section.

Official sources

  1. License: Apache-2.0
  2. open-policy-agent/opa on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/open-policy-agent-opa.svg)](https://hysenlabs.com/projects/open-policy-agent-opa)