Skip to main content

OPA Version Migration Guide

Overview​

Starting with OPAL v0.9.x, OPAL now ships with OPA v1 by default and supports both OPA v0 and OPA v1 compatibility modes. This guide explains how to migrate your Rego policies from OPA v0 to OPA v1 while ensuring a smooth transition.

What's Changed in OPA v1​

OPA v1 introduces several breaking changes that affect how Rego policies are written:

  • Package declarations: In OPA v0, you could write package authz or just package authz. In OPA v1, you must use package authz.v1 (with a version suffix).
  • Rule declarations: Rules now require explicit package versioning.
  • Import statements: Import paths have changed to be more explicit.
  • Built-in functions: Some built-in functions have been renamed or deprecated.

For more information, see OPA v1 Migration Guide.

Migration Strategy​

OPAL provides a gradual migration path:

  1. Enable v0 compatibility mode (default in OPAL v0.9.x)
  2. Test your policies with v0 compatibility enabled
  3. Migrate your policies to OPA v1 format
  4. Disable v0 compatibility mode when ready

Step 1: Enable v0 Compatibility Mode​

By default, OPAL v0.9.x enables v0 compatibility mode automatically. This means:

  • OPA will accept both v0 and v1 syntax
  • You can run your existing policies without immediate changes
  • The --v0-compatible flag is automatically added to OPA

Configuration​

The v0 compatibility mode is controlled by the OPAL_OPA_V0_COMPAT setting:

# Enable v0 compatibility (default in v0.9.x)
export OPAL_OPA_V0_COMPAT=true

# Disable v0 compatibility
export OPAL_OPA_V0_COMPAT=false

This setting merges with the inline OPA configuration:

# You can also set it in the inline OPA config
export OPAL_INLINE_OPA_CONFIG='{"v0_compatible": true}'

Note: If either OPAL_OPA_V0_COMPAT or OPAL_INLINE_OPA_CONFIG.v0_compatible is true, the --v0-compatible flag will be added to OPA.

Step 2: Test Your Current Policies​

With v0 compatibility enabled, verify that your existing policies work correctly:

  1. Start OPAL with your current policies
  2. Test authorization queries
  3. Check that all expected functionality works

Step 3: Migrate Your Policies​

Basic Package Migration​

OPA v0 syntax:

package authz

default allow = false

allow {
input.user == "admin"
}

OPA v1 syntax:

package authz.v1

default allow := false

allow if {
input.user == "admin"
}

Key Changes to Make:​

  1. Add .v1 suffix to package declarations
  2. Replace = with := for assignments
  3. Replace if statements with guard syntax
  4. Update import paths to include version suffixes

Example Migration​

Here's a more complex example:

Before (OPA v0):

package authz

import data.users

default allow = false

allow {
user := data.users[input.user]
user.role == "admin"
input.action == "delete"
}

has_permission[user] {
user := data.users[input.user]
user.role == "admin"
}

After (OPA v1):

package authz.v1

import data.users

default allow := false

allow if {
user := data.users[input.user]
user.role == "admin"
input.action == "delete"
}

has_permission contains user if {
user := data.users[input.user]
user.role == "admin"
}

Step 4: Disable v0 Compatibility Mode​

Once you've migrated all your policies:

  1. Set OPAL_OPA_V0_COMPAT=false or remove the setting (it defaults to false in v0.10.0+)
  2. Remove v0_compatible: true from your OPAL_INLINE_OPA_CONFIG
  3. Restart OPAL to apply the changes

Migration Timeline​

  • OPAL v0.9.x: OPAL_OPA_V0_COMPAT defaults to true for easier migration
  • OPAL v0.10.0+: OPAL_OPA_V0_COMPAT will be unconfigured (effectively false) by default

OPA bundled in the client image​

The permitio/opal-client image ships the OPA binary that runs as the inline engine. OPAL images released after 0.10.0-rc.2 ship OPA 1.21.4; earlier images shipped OPA 1.9.0. OPA tightened its compiler between those versions, so a policy that loaded on the old image can be rejected when the client loads it on the new one, with or without v0 compatibility mode:

  • OPA 1.19.0: stricter safety for :=. The right-hand side of an assignment is now a read. A policy that bound a variable backwards through := (for example x := y; x = 7, with y never assigned) now fails with rego_unsafe_var_error. See the 1.19.0 release notes.
  • OPA 1.20.0: new and and or keywords. import future.keywords now imports them too, so a policy with that import that uses and or or as a name no longer parses. See the 1.20.0 release notes.
  • OPA 1.21.0: empty composite literals are typed as empty, and in is type-checked. {}, [] and set() no longer have the type of "any collection", so selecting from or iterating one is a type error at compile time, and so is an in whose element can't be in the collection's type. See the 1.21.0 release notes.

Before upgrading, run the new OPA's checker against your policy repository:

docker run --rm -v "$PWD:/policy" openpolicyagent/opa:1.21.4-static check /policy
# if you run with OPAL_OPA_V0_COMPAT=true:
docker run --rm -v "$PWD:/policy" openpolicyagent/opa:1.21.4-static check --v0-compatible /policy

A policy that fails here would fail to load in the new client image. Fix it first, or stay on the previous image until you have.

Not caught by opa check. Two changes reject nothing at compile time, so check them by hand:

  • OPA 1.21.0 parses YAML with the YAML 1.2 core schema. Unquoted y, n, yes, no, on and off used to be booleans and are now strings (true and false are unaffected). Policies still compile, but a rule such as deny if data.flags.block == true stops matching when the YAML says block: yes, so a decision can change without any error. Search the policy repository for yaml.unmarshal, and for those unquoted words in YAML that OPA reads (data files, bundles, config), then quote them or use true/false.
  • OPA 1.21.3 changes bundle signing. If you pass OPA a config file (OPAL_INLINE_OPA_CONFIG, config_file) with a signing block: signatures are now required whenever the block is present, even without keyid, and a signing block with no entries under the top-level keys is now a startup error.

For the full list, read OPA's changelog from 1.10.0 to 1.21.4.

Troubleshooting​

Common Migration Issues​

  1. Package not found errors: Make sure to add .v1 suffix to package declarations
  2. Syntax errors: Update = to := and use guard syntax for if statements
  3. Import errors: Update import paths to include version suffixes

Debugging Tips​

  1. Check OPA logs for detailed error messages
  2. Use OPA's playground to test individual policies
  3. Enable verbose logging during migration:
    export OPAL_INLINE_OPA_LOG_FORMAT=full

Rollback Plan​

If you encounter issues:

  1. Re-enable v0 compatibility mode:
    export OPAL_OPA_V0_COMPAT=true
  2. Restart OPAL
  3. Fix any remaining policy issues
  4. Try migration again

Resources​