Skip to main content

What are Policies?

Policies are the heart of Cerbos - they define who can do what on which resources. Written in YAML, policies provide a declarative way to express complex authorization logic that evolves with your application. Cerbos policies are:
  • Version-controlled: Store them in Git alongside your application code
  • Testable: Write test suites to verify policy behavior
  • Composable: Import and reuse policy definitions across your system
  • Dynamic: Use conditions and expressions for context-aware decisions

Policy Types

Cerbos provides several policy types that work together:

Resource Policies

Define access rules for specific resource types (e.g., documents, projects, users)

Derived Roles

Dynamically assign roles based on contextual data at request time

Principal Policies

Override permissions for specific users or principals

Conditions

Express complex authorization logic using CEL expressions

Policy Structure

Every Cerbos policy follows this basic structure:
---
apiVersion: api.cerbos.dev/v1
# Optional: Human-readable description
description: |
  Describes what this policy does

# Optional: Import constants and variables
variables:
  pending_status: '"PENDING_APPROVAL"'
  
# Policy definition (resourcePolicy, derivedRoles, or principalPolicy)
resourcePolicy:
  resource: leave_request
  version: "default"
  rules:
    - actions: ["view", "create"]
      effect: EFFECT_ALLOW
      roles: ["employee"]

Key Components

Always set to api.cerbos.dev/v1. This specifies the policy schema version.
Human-readable text explaining the policy’s purpose. Useful for documentation but not evaluated by Cerbos.
Define reusable expressions at the top level. Can be local definitions or imports from export policies.
The main policy definition - either resourcePolicy, derivedRoles, principalPolicy, exportConstants, or exportVariables.

How Policies Work Together

Cerbos evaluates policies in a specific order to determine access:

Evaluation Order

  1. Principal Policies (if defined) - Specific overrides for individual users
  2. Resource Policies - General rules for the resource type
  3. Derived Roles (if referenced) - Dynamic role assignment based on context
  4. Conditions - Fine-grained attribute-based rules
Principal policies take precedence over resource policies. An explicit DENY in a principal policy will override an ALLOW in a resource policy.

Effects: ALLOW vs DENY

Every policy rule must specify an effect:
  • EFFECT_ALLOW: Grants permission for the action
  • EFFECT_DENY: Explicitly denies permission (takes precedence)
rules:
  # Allow admins to do everything
  - actions: ["*"]
    effect: EFFECT_ALLOW
    roles: ["admin"]
  
  # Explicitly deny deletion of archived resources
  - actions: ["delete"]
    effect: EFFECT_DENY
    condition:
      match:
        expr: request.resource.attr.archived == true
DENY always wins. If any rule evaluates to DENY, the request is denied even if other rules would allow it.

Scoping Policies

Policies can be scoped to apply only in specific contexts:
resourcePolicy:
  resource: document
  version: "default"
  scope: "acme.corp"  # Only applies to resources in the acme.corp scope
  rules:
    # ...
Scopes enable:
  • Multi-tenancy: Different rules per tenant/organization
  • Environment-specific policies: Dev, staging, prod variations
  • Hierarchical rules: Scope inheritance with acme.corp.engineering
Learn more in the Server Configuration documentation.

Schema Validation

Resource policies can reference JSON schemas to validate request data:
resourcePolicy:
  resource: leave_request
  version: "default"
  schemas:
    principalSchema:
      ref: cerbos:///principal.json
    resourceSchema:
      ref: cerbos:///resources/leave_request.json
  rules:
    # ...
Schemas help catch errors early and provide IDE autocompletion.

Policy Organization

Organize your policies using a clear directory structure:
policies/
├── derived_roles/
│   ├── common_roles.yaml
│   └── admin_roles.yaml
├── resource_policies/
│   ├── document.yaml
│   ├── project.yaml
│   └── leave_request.yaml
├── principal_policies/
│   └── ceo_overrides.yaml
└── exports/
    ├── constants.yaml
    └── variables.yaml
Use descriptive file names that match your resource or role names for easier navigation.

Best Practices

1

Start with Resource Policies

Define the general access rules for each resource type first. These form the foundation of your authorization model.
2

Extract Common Patterns

If you’re repeating the same conditions, create derived roles or export variables to make policies DRY.
3

Use Principal Policies Sparingly

Principal policies are powerful but can make debugging harder. Use them only for necessary overrides.
4

Test Everything

Write comprehensive test suites for all policies. See Policy Testing for details.
5

Version Your Policies

Use policy versions to manage changes over time. Old clients can continue using older policy versions.

Quick Examples

---
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  resource: document
  version: "default"
  rules:
    - actions: ["view", "create"]
      effect: EFFECT_ALLOW
      roles: ["user"]
    
    - actions: ["*"]
      effect: EFFECT_ALLOW
      roles: ["admin"]

Next Steps

Resource Policies

Learn how to define access rules for your resources

Derived Roles

Create dynamic roles based on request context

Conditions

Write powerful expressions for fine-grained control

Testing

Test your policies with cerbos compile

FAQ

Roles are static roles assigned to the principal (e.g., from your identity provider). Derived roles are dynamically computed based on the request context - for example, making someone an “owner” if they created the resource.
Use resource policies for general rules that apply to all users. Use principal policies only when you need to override the general rules for specific individuals (e.g., temporarily granting the CEO access to everything).
Yes. Cerbos evaluates all matching rules. If any rule results in DENY, the request is denied. Otherwise, if at least one rule results in ALLOW, the request is allowed.
Use export variables to define reusable expressions, then import them in your resource or principal policies. See Conditions for examples.
Policies don’t inherit from each other by default, but scoped policies do cascade. A policy with scope acme.corp.engineering will inherit rules from acme.corp and the root policy. You can control this with scopePermissions.