Deployment model
A self-hosted instance is the policyarc-server Helm chart installed in your Kubernetes cluster. The chart needs Kubernetes 1.24 or later. This page lists what the chart runs, what you run yourself, and the values you must set. It describes chart version 0.65.0. The hosted service is not described here.
What the chart runs
These are the objects the chart renders with its default values, for a release named policyarc.
| Object | Name | What it is |
|---|---|---|
| Deployment | policyarc-policyarc-server-authorization | The authorization server. It serves the OAuth and OpenID Connect endpoints, the Admin API and admin UI, the managed gateway and the policy bundle. authServer.replicaCount sets its replicas, 1 by default. |
| Deployment | policyarc-policyarc-server-opa | OPA, image openpolicyagent/opa at the tag in opa.image.tag, 1.10.0 by default. opa.replicaCount sets its replicas, 1 by default. |
| Service | policyarc-policyarc-server-authorization and policyarc-policyarc-server-opa | Cluster-internal addresses on ports 8080 and 8181 |
| Ingress | policyarc-policyarc-server-authorization | Sends the host in ingress.hosts to the server, through the class in ingress.className, traefik by default |
| Secret | policyarc-policyarc-server-credentials | When secret.create is true. It always holds the database password and the bundle key, and holds the admin key, the pepper, the credential key and the link secret only when their values are set. |
| ConfigMap | policyarc-policyarc-server-opa-config | OPA's configuration, which points it at the server's bundle endpoint |
| NetworkPolicy | One for the server and one for OPA | On by default, networkPolicy.enabled |
| PodDisruptionBudget | One for the server and one for OPA | At most one pod of each unavailable during a voluntary disruption such as a node drain |
Every pod runs as a non-root user with a read-only root file system, drops all Linux capabilities and mounts no service account token. Each has a writable /tmp volume.
These are off by default: a HorizontalPodAutoscaler (authServer.autoscaling.enabled), a headless Service for addressing single pods (authServer.headlessService.enabled), a separate gateway process (gateway.enabled) and a telemetry collector (otelCollector.enabled).
What you run and operate
| You run | What it needs | Read more |
|---|---|---|
| PostgreSQL 16 | It holds all instance state. Each server replica opens up to 20 connections, 19 pooled and 1 for signals between replicas. That one needs a direct route, so a transaction-mode pooler cannot carry it. Keep sslmode=require and the TimeZone=UTC option in authServer.datasource.url. | Replicas and upgrades |
| Database backups | The chart and the server take no backups | Availability and recovery |
| An ingress controller and TLS | The chart's ingress.tls is empty, so TLS is set up at your controller or in that value | Reference architecture |
| The secrets | The server refuses to start without them | Harden an instance |
| Log collection | The server and OPA write to standard output. OPA's lines hold full decision inputs. | Data handling |
| Metrics scraping | /actuator/prometheus answers without a key, so block it at the ingress | Harden an instance |
| Upgrades | Run helm upgrade. Each replica runs database migrations at start. A new image does not replace the policy rules an instance already has. | Replicas and upgrades |
| Image pulls | Set imagePullSecrets if your registry needs credentials |
Values you must set
| Value | What it is |
|---|---|
authServer.datasource.url | The JDBC URL of your database. The default points at a host named postgres. |
secret.datasourcePassword | The database password. authServer.datasource.username is pbac by default. |
authServer.issuer | The public URL clients use. Tokens and discovery name it. |
ingress.hosts | The host name your ingress serves |
secret.adminApiKey, secret.adminPepper, secret.credentialKey, secret.bundleApiKey, secret.linkStateSecret | The five server secrets. Mint each one per install. |
To keep secrets out of Helm values, set secret.create to false and secret.existingName to a Secret you manage. That Secret needs six keys, SPRING_DATASOURCE_PASSWORD, PBAC_BUNDLE_API_KEY, PBAC_ADMIN_API_KEY, PBAC_ADMIN_PEPPER, PBAC_CREDENTIAL_KEY and PBAC_GATEWAY_LINK_STATE_SECRET, because the chart then reads each one from it.
The chart renders with every secret blank, because its Secret template has no required checks. The server checks the values when it starts. A blank server secret stops the start with a message that names the setting, see Harden an instance. A blank database password stops it earlier. We started the server image with all six values blank, and it exited at its database migration with The server requested SCRAM-based authentication, but no password was provided, naming no Helm value.
Check what the chart renders
Render the chart before you install it, and compare the objects with the table above. Set POLICYARC_CHART to the chart directory. The block needs helm.
export POLICYARC_CHART="path/to/policyarc-server"
rendered=$(helm template policyarc "$POLICYARC_CHART")
kinds=$(grep '^kind: ' <<<"$rendered" | sort | uniq -c | awk '{print $3 "=" $1}' | tr '\n' ' ')
echo "$kinds"
[ "$kinds" = "ConfigMap=1 Deployment=2 Ingress=1 NetworkPolicy=2 PodDisruptionBudget=2 Secret=1 Service=2 " ] ||
{ echo "Expected the objects in the table above, got: $kinds" >&2; false; }
deployments=$(awk '/^kind: Deployment/ {d = 1; next} d && /^ name:/ {print $2; d = 0}' <<<"$rendered" | sort | tr '\n' ' ')
[ "$deployments" = "policyarc-policyarc-server-authorization policyarc-policyarc-server-opa " ] ||
{ echo "Expected the authorization server and OPA Deployments, got: $deployments" >&2; false; }
With chart version 0.65.0 it lists one each of ConfigMap, Ingress and Secret, and two each of Deployment, NetworkPolicy, PodDisruptionBudget and Service. It renders no StatefulSet, because the chart runs no database.
Send feedback about this page