Writing Rego Policies with OPA: Enforcing Governance and Compliance

Written By:
Founder & CTO
June 19, 2025

In the evolving world of cloud-native applications, security and compliance are no longer afterthoughts, they’re baked into every stage of development, deployment, and operation. For developers tasked with delivering reliable, secure software, manually enforcing policies across systems is tedious, error-prone, and nearly impossible to scale. This is where Open Policy Agent (OPA) enters the picture.

OPA is a lightweight, open-source policy-as-code engine that empowers developers to define, manage, and enforce policies using a declarative language called Rego. With Open Policy Agent, you can decouple policy logic from application logic, apply consistent governance rules across multiple systems (like Kubernetes, Terraform, CI/CD, microservices), and boost auditability, security, and agility across your stack.

In this detailed blog tailored for developers, we’ll dive deep into how to write Rego policies with OPA, the benefits it brings to governance and compliance, its distinct advantages over traditional policy enforcement methods, and how you can practically apply OPA in your development workflow to build smarter, safer software.

What Is Open Policy Agent (OPA)?

Open Policy Agent is an open-source general-purpose policy engine maintained under the Cloud Native Computing Foundation (CNCF). It allows developers to write policies in a high-level declarative language (Rego) and apply them consistently across distributed systems. Unlike traditional enforcement systems tightly coupled to specific platforms or hardcoded into apps, OPA can be embedded anywhere, from Kubernetes admission controllers to API gateways, service meshes, and CI/CD pipelines.

OPA works by evaluating policy decisions against structured input data, typically JSON, and returning a decision (true/false, allow/deny, or any custom value). The policies themselves are version-controlled, testable, and auditable, making OPA a prime tool for implementing governance, risk, and compliance (GRC) in code.

Developers can either embed OPA as a sidecar or call it via its REST API to evaluate policies without writing custom authorization logic. This approach is especially beneficial in modern DevSecOps environments where code velocity must align with security and compliance mandates.

Why Developers Should Love OPA

Let’s break down why Open Policy Agent is a must-have in every modern developer’s toolkit, especially for those working in environments that require continuous deployment, dynamic infrastructure, and fine-grained access control.

1. Decouple Logic and Governance

One of the most compelling reasons developers love OPA is the ability to decouple business and compliance rules from application logic. Rather than writing conditional access or control logic scattered throughout the codebase (if user.isAdmin && resource.isSensitive then...), developers can define these rules centrally using Rego.

This separation improves modularity, simplifies code maintenance, and eliminates redundant logic across services. It also empowers security teams to write and update policies independently without needing to push application code changes, reducing friction between dev and ops.

2. High Performance with Low Overhead

OPA is incredibly lightweight, typically running as a sidecar, library, or daemon. When deployed alongside an application, OPA evaluates policies in-memory, meaning it doesn’t require constant roundtrips to external services or databases. This makes it ideal for latency-sensitive environments.

Despite its small footprint (under 50 MB), OPA can handle thousands of policy evaluations per second with response times in the microseconds range. This high performance, combined with its compactness, means you can deploy OPA across the board, from edge devices to container orchestrators, without sacrificing speed or scalability.

3. Unified Governance Across the Stack

Traditionally, enforcing policies across heterogeneous systems (like Kubernetes, CI/CD, and cloud APIs) required specialized tools for each layer. OPA simplifies this by offering a unified policy language and engine that can be reused across different domains.

With Open Policy Agent, developers can define one consistent policy model and apply it to:

  • Kubernetes admission control (via Gatekeeper)

  • Terraform plan evaluations (via Conftest)

  • Microservices APIs (via Envoy + OPA plugin)

  • CI/CD pipelines (as pre-merge checks)

  • Service meshes and proxies

This unified governance architecture not only improves policy reuse but also prevents compliance drift, where one system lags behind others in terms of enforcement.

4. Auditability and Policy-as-Code

OPA policies are written as code (in Rego), version-controlled in Git, and testable through unit tests. This policy-as-code approach enables developers and security engineers to review changes via pull requests, apply CI/CD to policies, and ensure traceability over every change.

OPA also provides decision logs, structured outputs that describe why a decision was made, what data was used, and which rule was triggered. This level of transparency is essential for passing audits, debugging policy issues, and maintaining trust between security and dev teams.

5. Modularity and Reusability

Rego supports modular design patterns, allowing policies to be broken into small, composable files that can be imported into larger policies. This encourages clean abstractions and code reuse.

For example, a developer can define common helper functions like is_admin(user) or has_valid_token(input) and reuse them across dozens of policy files. This not only reduces duplication but also ensures that logic changes are applied uniformly wherever the helper is used.

How OPA Beats Traditional Approaches

Developers used to rely on several traditional methods to enforce access control and compliance:

  • Hardcoded logic in applications

  • Platform-specific access control lists (ACLs)

  • IAM services or role-based access control (RBAC)

  • Custom scripts written in Go, Python, or Bash

While these methods work to an extent, they suffer from major limitations:

1. Hardcoded logic is brittle and non-reusable. Each system implements its own logic, making updates difficult and error-prone. A rule change requires re-deploying the entire application.

2. IAM/RBAC tools offer coarse-grained access control. They often don’t support contextual rules like “user must be on the VPN” or “request must come during business hours.”

3. Scripting lacks consistency and auditability. Custom scripts written for different tools (Terraform, Helm, CI, etc.) grow unmanageable over time and lack version control or testing.

OPA addresses these limitations by offering a centralized, consistent, extensible, and testable framework that works across systems and scales with your infrastructure.

Building Your First Rego Policy

Let’s walk through a simple example: only allow admins to deploy to the production cluster.

In this policy, the default behavior is to deny all actions. The allow rule only returns true if both the user is “admin” and the environment is “prod”. This is a basic form of attribute-based access control (ABAC).

Now feed this input to OPA:

{

  "user": "alice",

  "environment": "prod"

}

OPA will evaluate the input against the policy and return:

{

  "allow": false

}

No need to change application code. Just update the .rego file and reload the policy. Developers can test this locally using opa eval or push it through a policy management pipeline for distributed updates.

Scaling to Complex Governance Scenarios

In real-world systems, governance often involves multiple attributes, exceptions, and dynamic contexts. Rego supports advanced constructs like imports, data access, and partial evaluation to handle this.

Here’s an expanded policy:

In this policy, we’re checking:

  • The user’s role from external data (roles)

  • That the environment isn’t production

  • That the request is within business hours (office_hours.allowed_time)

Using imported data (data.roles, data.office_hours) allows dynamic updates without rewriting logic. This is perfect for organizations that maintain role maps or work calendars in JSON or external services.

Best Practices for Writing Rego Policies

1. Keep Policies Purpose-Specific
Each .rego file should address a single domain: access control, resource quotas, or compliance rules. Avoid overloading a single file with multiple responsibilities.

2. Modularize for Reuse
Use Rego packages and imports to create reusable logic blocks. For example, utils.has_valid_token can be used across multiple services.

3. Externalize Data
Don’t hardcode user roles or config thresholds in the policy. Store them in JSON files or fetch them dynamically (if enabled). This simplifies updates and testing.

4. Restrict Built-in Functions
Disable dangerous functions like http.send unless absolutely needed. This prevents policy abuse and keeps evaluations secure.

5. Add Policy Tests
Use OPA’s built-in test framework to write *_test.rego files that verify policy behavior. Test inputs should cover positive and negative scenarios.

6. Log and Monitor Decisions
Enable decision logging to capture policy evaluations. These logs help with debugging, auditing, and proving compliance in regulated environments.

7. Use Policy Distribution Tools
In large-scale systems, push policy updates via tools like OPAL or Conftest. These tools sync policy changes across environments consistently.

Real-World Workflows with OPA

Kubernetes Admission Control
Use OPA as a Gatekeeper to enforce policies like:

  • Containers must set CPU/memory limits

  • Pods must have specific labels (e.g., cost center)

  • Disallow usage of hostPath volumes in prod

API Gateway Integration
OPA can sit next to gateways like Kong or Envoy and enforce fine-grained API authorization based on user role, request path, and more.

CI/CD Compliance Checks
Use tools like Conftest to validate IaC files (Terraform, Dockerfile, Helm charts) during pipeline runs. Reject builds that violate policies.

Service Mesh and Microservices
Enforce runtime policies between services: e.g., “Service A can only call Service B if user is in org X and request includes token Y.”

Lightweight But Still Effective

Despite its vast capabilities, OPA remains remarkably light. It’s written in Go, uses minimal memory, and loads policy and data into memory for fast evaluation. For most use cases, OPA adds less than 1 ms latency.

This makes it perfect for:

  • Edge computing

  • Serverless environments

  • Embedded policy checks in IoT devices

  • Low-latency gateways

OPA delivers the power of a full policy engine without the heavyweight infrastructure associated with traditional compliance tools.

Getting Started With OPA
  1. Download OPA binary from openpolicyagent.org

  2. Write your first .rego policy

  3. Test with opa eval or the OPA Playground

  4. Integrate into apps via REST, sidecar, or SDK

  5. Add policy tests and decision logging

  6. Deploy policies with CI/CD and monitor performance

Final Thoughts

As software development becomes more complex, distributed, and regulated, developers must think beyond just features, they must build secure, compliant, auditable systems from the ground up.

Open Policy Agent empowers developers to do just that. With its declarative language (Rego), centralized policy management, blazing-fast performance, and compatibility across systems, OPA enables a new era of developer-led governance and compliance.

For modern development teams that value agility, security, and scale, OPA is not just a tool, it’s a paradigm shift.