Migrating from legacy workspaces
This guide covers migrating from the legacy Cerbos Hub workspace architecture to the current system based on policy stores and deployments. It applies to both service PDPs and embedded PDPs.
| If you would prefer a guided migration, contact support@cerbos.dev. We are happy to walk you through the process. |
What the new architecture provides
The legacy system coupled a single Git repository to a single workspace, with deployment labels defined in .cerbos-hub.yaml and a workspace secret for bundle encryption. The current architecture removes these constraints.
- Multi-store composition
-
Deployments can combine policies from multiple independent policy stores. Teams, services, or tenants can own their policies separately while Cerbos Hub merges them at build time. The legacy system was limited to a single repository per workspace.
- Source-agnostic ingestion
-
Policy stores accept input from Git repositories, CI/CD pipelines, the Cerbos Hub API, SDKs, the
cerbosctlCLI, or browser upload. The legacy system required a GitHub repository. See supported ingestion methods. - No workspace secret
-
Bundle encryption is managed entirely by Cerbos Hub. There is no secret to generate, distribute, rotate, or lose. PDP configuration requires only a client ID, client secret, and deployment ID. See deploying a PDP.
- Deployment-scoped credentials
-
Credentials are scoped to individual deployments rather than an entire workspace. This provides a narrower blast radius when credentials are rotated or revoked. See obtaining credentials.
- Embedded PDP rules
-
Each deployment can define multiple ePDP rules with independent policy filtering (by resource, action, scope, role, and version), authentication requirements, and IP allowlists. The legacy system included all annotated policies in a single bundle with no access controls.
- Automatic build pipeline
-
Every change to a policy store triggers compilation, test execution, and bundle distribution. The legacy system required
.cerbos-hub.yamllabel mappings to control which Git references produced builds. See build life cycle.
Migration overview
Migration involves five phases:
-
Create a new workspace in Cerbos Hub
-
Create a policy store and connect it to your Git repository
-
Create a deployment using that policy store
-
Update service PDP configuration to use the new credentials and deployment ID
-
Update embedded PDP integration (if applicable)
Each phase can be completed independently. Legacy and current PDPs can run in parallel during the transition.
Architecture changes
| Aspect | Legacy | Current |
|---|---|---|
Policy source |
Workspace (single Git repository, single policy source) |
One or more policy stores per deployment |
Bundle versioning |
Deployment labels in |
Deployment ID; each policy store change triggers a new build automatically |
Bundle encryption |
Workspace secret (asymmetric key pair) |
Managed by Cerbos Hub; no workspace secret required |
Credentials |
Workspace-scoped client ID, client secret, and workspace secret |
Deployment-scoped client ID and client secret |
Embedded PDP bundle addressing |
URL with |
Opaque ePDP rule ID |
Embedded PDP policy filtering |
Per-policy YAML annotation ( |
Per-rule configuration in Hub UI (resources, actions, scopes, roles, versions) |
Audit log collection |
Same credentials as bundle distribution |
Same credentials as bundle distribution (no workspace secret) |
Phase 1: Create a new workspace
Policy stores and deployments are only available in new workspaces. Your legacy workspace cannot be upgraded in place, so you need to create a new one.
-
Sign in to Cerbos Hub
-
Create a new workspace
Phase 2: Create a policy store
A policy store replaces the workspace as the container for your policies. If your legacy workspace was connected to a GitHub repository, the policy store connects to the same repository.
-
In the new workspace, navigate to Policy stores and click New store
-
Enter a name for the store (e.g. the same name as your legacy workspace)
-
Select GitHub repository as the source and authorize access if prompted
-
Select the same repository your legacy workspace used
-
Configure the branch to track
If your legacy .cerbos-hub.yaml defined a label pointing to a specific branch (e.g. main), use that branch as the policy store’s tracked branch.
# .cerbos-hub.yaml (legacy)
apiVersion: api.cerbos.cloud/v1
labels:
latest:
branch: main
In this example, create a policy store tracking the main branch. The label abstraction is no longer needed — the policy store tracks the branch directly.
If your legacy setup used multiple labels pointing to different branches (e.g. production on main and staging on develop), create a separate policy store for each branch, then reference each in the appropriate deployment.
See Policy stores and GitHub integration for details.
Subdirectory configuration
If your policies are in a subdirectory of the repository, specify the path when configuring the GitHub connection. The policy store syncs only that directory and its children.
Non-GitHub sources
If your policies are not in GitHub, other ingestion methods are available: CI/CD pipelines, Cerbos Hub SDKs, the cerbosctl CLI, or browser upload. See Supported ingestion methods.
Phase 3: Create a deployment
A deployment replaces the combination of workspace + deployment label. It packages policies from one or more policy stores into versioned bundles and distributes them to connected PDPs.
-
Navigate to Deployments and click New deployment
-
Select the policy store created in Phase 2
-
Click Create
Cerbos Hub runs an initial build. When it completes, note the deployment ID from the deployment detail page.
Generate client credentials
-
On the deployment page, select the Client credentials tab
-
Click Generate a client credential
-
Choose Read & Write if you need audit log collection, or Read only for bundle distribution only
-
Save the client ID and client secret — the secret is shown only once
These credentials replace the legacy workspace-scoped credentials and workspace secret.
Phase 4: Update service PDP configuration
The PDP configuration changes in two ways:
-
CERBOS_HUB_BUNDLE(label name) is replaced byCERBOS_HUB_DEPLOYMENT_ID -
CERBOS_HUB_WORKSPACE_SECRETis no longer required
Legacy configuration
docker run --rm --name cerbos \
-p 3592:3592 -p 3593:3593 \
-e CERBOS_HUB_BUNDLE="latest" \
-e CERBOS_HUB_WORKSPACE_SECRET="..." \
-e CERBOS_HUB_CLIENT_ID="..." \
-e CERBOS_HUB_CLIENT_SECRET="..." \
ghcr.io/cerbos/cerbos:latest server
Updated configuration
docker run --rm --name cerbos \
-p 3592:3592 -p 3593:3593 \
-e CERBOS_HUB_DEPLOYMENT_ID="..." \
-e CERBOS_HUB_CLIENT_ID="..." \
-e CERBOS_HUB_CLIENT_SECRET="..." \
ghcr.io/cerbos/cerbos:latest server
Use the deployment ID and credentials from Phase 3.
YAML configuration file
hub:
credentials:
clientID: "..."
clientSecret: "..."
workspaceSecret: "..."
storage:
driver: hub
hub:
remote:
bundleLabel: latest
hub:
credentials:
clientID: "..."
clientSecret: "..."
storage:
driver: hub
hub:
remote:
deploymentID: "..."
The workspaceSecret and bundleLabel fields are removed. The deploymentID field replaces bundleLabel.
Audit log collection
If audit log collection was enabled, the audit configuration block is unchanged:
audit:
backend: hub
hub:
storagePath: "/var/cerbos/audit-buffer"
The audit backend uses the same deployment-scoped credentials configured in the hub.credentials block. No additional credential changes are needed.
| Existing audit log data stored under your legacy workspace is not automatically migrated to the new deployment. Contact the Cerbos team at support@cerbos.dev to arrange migration of your historical audit logs. |
Cerbos version requirement
The current deployment system requires Cerbos version 0.51.0 or later. If your PDPs are running an older version, upgrade before switching to the new credentials.
Rolling migration
Legacy and current PDPs can run in parallel. To migrate a fleet incrementally:
-
Deploy updated configuration to a subset of instances
-
Verify those instances connect to Cerbos Hub and receive the correct bundle (check the Decision points tab on the deployment page)
-
Roll out to the remaining instances
Phase 5: Update embedded PDP integration
This phase applies only if you use the embedded PDP (WebAssembly-based authorization in browsers, edge functions, or other JavaScript environments). If you only use service PDPs, skip to Cleanup.
The embedded PDP requires new SDK packages and a different bundle addressing model.
Create an ePDP rule
Before updating client code, create an ePDP rule on the deployment:
-
Navigate to the deployment in Cerbos Hub
-
Select the Embedded PDP rules tab
-
Click Create rule
-
Configure policy filtering (see Policy filtering below)
-
Configure access controls (see Access controls below)
-
Click Save rule
-
Copy the rule ID from the rule card
Policy filtering
The legacy system used a YAML annotation on individual policies to control inclusion in the bundle:
apiVersion: api.cerbos.dev/v1
metadata:
annotations:
hub.cerbos.cloud/embedded-pdp: "true"
resourcePolicy:
version: default
resource: purchase_order
The current system filters policies through the ePDP rule configuration in Cerbos Hub. The annotation is no longer used.
To replicate your existing filtering:
-
Identify which policies had the
hub.cerbos.cloud/embedded-pdp: "true"annotation -
Note the resource types, actions, roles, and scopes those policies cover
-
Configure equivalent filters on the ePDP rule using the Resources and actions, Scopes, Roles, and Versions settings
If no policies in your legacy setup were annotated (all policies were included), leave all filters set to All on the new rule.
The hub.cerbos.cloud/embedded-pdp annotation can be removed from your policy files after migration is complete. It has no effect in the current system.
|
See Filtering policies for details on each filter type.
Access controls
The legacy embedded PDP did not support authentication or IP restrictions on bundle downloads. The current system supports both.
| Authentication |
If the bundle is served to end-user browsers and the authorization logic is not sensitive, Public access is appropriate. If the bundle is fetched server-side (Node.js, edge workers), use Client credential and store credentials in environment variables or a secrets manager. |
| IP allowlist |
If bundle access should be restricted to specific network ranges, configure CIDR entries on the rule. |
See Security controls for details.
Update SDK packages
Remove the legacy package and install the replacements:
npm uninstall @cerbos/embedded
npm install @cerbos/embedded-client @cerbos/embedded-server
If the rule uses client credential authentication, also install the @cerbos/hub package:
npm install @cerbos/hub
Update client initialization
import { AutoUpdatingLoader, Embedded } from "@cerbos/embedded";
const cerbos = new Embedded(
new AutoUpdatingLoader(
"https://lite.cerbos.cloud/bundle?workspace=...&label=...",
),
);
import { Embedded } from "@cerbos/embedded-client";
import wasm from "@cerbos/embedded-server/server.wasm?init"; (1)
const cerbos = new Embedded({
policies: { ruleId: "<RULE_ID>" },
wasm,
});
| 1 | The ?init import suffix is Vite-specific. See WebAssembly module loading below for other environments. |
Replace <RULE_ID> with the rule ID from Cerbos Hub.
The AutoUpdatingLoader and the bundle URL are no longer used. The SDK fetches bundles and polls for updates based on the rule ID.
WebAssembly module loading
The legacy SDK bundled the WebAssembly runtime internally. The current SDK requires explicit loading of the WASM module. The mechanism depends on your build tooling:
| Environment | Import |
|---|---|
Vite |
|
Webpack |
|
Rspack |
|
Next.js (Turbopack) |
Copy WASM to |
Node.js |
|
See Loading the WebAssembly module for all options.
Authenticated access
If the rule requires client credentials:
import { Embedded } from "@cerbos/embedded-client";
import { credentialsFromEnv } from "@cerbos/hub";
import wasm from "@cerbos/embedded-server/server.wasm?init";
const cerbos = new Embedded({
policies: {
ruleId: "<RULE_ID>",
credentials: credentialsFromEnv(),
},
wasm,
});
This reads CERBOS_HUB_CLIENT_ID and CERBOS_HUB_CLIENT_SECRET from process.env.
Authorization check API
The authorization check methods (isAllowed, checkResource, checkResources) are unchanged between the legacy and current SDKs. Existing check calls do not require modification. Run your test suite to confirm authorization decisions match the legacy behavior.
The current SDK also supports planResources, which was not available in the legacy embedded PDP.
New capabilities
The current ePDP SDK adds several capabilities not present in the legacy version:
| Dynamic scopes |
Clients specify scopes at fetch time, so each tenant gets only its applicable policies without a dedicated rule. See Dynamic scopes. |
| Deferred activation |
Download bundle updates without applying them immediately. See Deferred activation. |
| Update notifications |
Register callbacks for bundle update success or failure. See Update notifications. |
| Decision callbacks |
Capture authorization decisions locally via |
| Query planning |
Use |
| Multiple rules per deployment |
Serve different bundle configurations to different clients. See ePDP rules. |
Cleanup
After confirming all PDPs are running on the new configuration:
-
Remove the
.cerbos-hub.yamldeployment labels file from your repository (this was only used by the legacy workspace system) -
Remove the
hub.cerbos.cloud/embedded-pdpannotations from policy files, if present -
Verify that the
@cerbos/embeddedpackage is no longer referenced in your codebase (embedded PDP only) -
Revoke the legacy workspace client credentials
-
Delete or archive the legacy workspace