zynactradocs

Deploying

Run Cerbos next to each service, ship decision logs, migrate the access schema, keep caches fresh, and upgrade safely.

Cerbos as a sidecar

Each service instance gets its own Cerbos PDP on localhost: a decision costs a loopback hop, and a Cerbos outage only affects its own instance. Cerbos is stateless; it reads the generated policies and nothing else.

cerbos.yaml
server:
  httpListenAddr: ":3592"
storage:
  driver: disk
  disk:
    directory: /policies
    watchForChanges: true
schema:
  enforcement: reject
audit:
  enabled: true
  accessLogsEnabled: false
  decisionLogsEnabled: true
  backend: file
  file:
    path: stdout
  • Policies: ship out.policies (including _schemas/ and orgs/) with the service image, or mount them read-only, so a service and the policies it was built with always deploy together.
  • Schema enforcement reject: a check with missing or extra attributes is denied, never evaluated against partial data.
  • Pin the version zynactra/access tests against: ghcr.io/cerbos/cerbos:0.57.0.
  • Leave engine.lenientScopeSearch off, so a missing org rule policy denies instead of falling back to the product policy.
  • Readiness: gate the service's readiness on the sidecar with CerbosHTTP.Healthy(ctx) (Go) or cerbos.healthy() (TypeScript), which call /_cerbos/health.

On Kubernetes this is a second container in the pod; with Docker Compose, a second service (see the examples' docker-compose.yml).

Decision logs

With the config above, Cerbos writes every check and list plan to stdout as JSON: who asked, about what, the effect, the org scope, and the outputs naming the scope that allowed it or the rule that denied it. Ship them to your log pipeline: they answer "who looked at what" for audits. Checks send only the permissions they decide, so entries don't carry a person's whole access.

The database

The access schema lives in the product's own Postgres. Apply its migrations before the service starts serving: migrate.Apply(ctx, db) / migrate(pool) at startup, or zaccess migrate as a deploy step. Migrations are append-only and safe to run on every start.

Then sync the catalog: SyncCatalog / syncCatalog at startup (or zaccess sync) records the manifest's permissions and system roles. zaccess sync -org <id> also seeds the system roles into an org.

The service's database user needs to read and write the access tables and LISTEN; it never needs to alter the change log, which the database refuses anyway.

Cache invalidation

Run the NOTIFY listener on every instance: pqlisten.Listen(ctx, dsn, access, log) (Go) or listen(databaseUrl, access) (TypeScript). It holds one connection and drops cached grants when an admin change commits. If the connection drops, the whole cache is cleared and the listener reconnects. With a connection pooler in transaction mode, point the listener at the database directly: LISTEN needs a session.

Timeouts and failure

Each Cerbos call has a 250 ms timeout and one retry. A failure denies (reason unavailable, 503 from the access API); there is no allow fallback. Watch the rate of unavailable denials, and alert on it.

Upgrading

  • zynactra/access: the four npm packages and the Go module share a version. Upgrade them together, run zaccess gen with the matching CLI, and read the changelog: a major version says what to change.
  • Manifests: deploy generated policies together with the code that uses them. Removing a permission is a two-step change if old instances are still serving: stop using it in code first, remove it from the manifest in a later deploy.
  • Cerbos: upgrade the pinned version only when zynactra/access does.
Edit on GitHub

On this page