Skip to main content

On-Premises Reference

Enterprise on-premises license required

The on-premises installer is available to Enterprise customers with an on-premises license. To get a license, contact Permit.io.

This page lists the installer flags, the Helm releases, the kubectl commands, and the values.yaml configuration keys of a self-hosted Permit Platform. It is for operators who install and configure the platform. For the installation procedure, see the installation guide. For step-by-step procedures such as upgrades, backups, support bundles, and uninstalling, see the management guide.

Installer command reference

The installer is scripts/install-permit-platform.sh in the installer package. Run it from the package root. Pass exactly one target flag on every run.

FlagEffect
--gkeDeploy to an existing Kubernetes cluster in your current kubectl context: Google GKE, Amazon EKS, Azure AKS, or self-managed. The installer skips cluster creation and image loading and pulls the images from global.imageRegistry. The installer accepts this flag, but --help doesn't list it.
--openshiftDeploy to an OpenShift cluster, such as Red Hat OpenShift Service on AWS (ROSA) or OpenShift Container Platform (OCP).
--kindCreate a local Kind cluster named permit-platform-on-prem for development, load the images into it, install an NGINX ingress controller, and generate self-signed certificates.
(no target flag)Run the Kind steps: create the permit-platform-on-prem Kind cluster and read the image tag from that cluster's control-plane container. Unlike --kind, the installer does not install the NGINX ingress controller or generate certificates.
-n, --namespace NAMESPACEInstall into NAMESPACE. Default: permit-platform.
--generate-tlsGenerate TLS certificates.
--skip-tls-checkSkip the TLS check. Use it when TLS terminates outside the cluster ingress, for example at a cloud load balancer or with cert-manager.
--skip-imagesDon't load images from the package. Required when the images are already in your private registry.
--skip-openshift-registryOn OpenShift, don't configure the internal OpenShift registry. Use it with a custom registry.
--dry-runShow what the installer would deploy without applying it.
--yesConfirm a Keycloak schema migration without a prompt. Required for non-interactive runs when the Keycloak version changes.
-h, --helpPrint the installer help.
A run without a target flag fails on an existing cluster

The --help output lists a run without a target flag as the production Kubernetes target, and the installer logs Deployment Target: Production Kubernetes (EKS/GKE/AKS). The script then runs the Kind path: it tries to create a Kind cluster and to read the backend image tag from a container named permit-platform-on-prem-control-plane. On an existing EKS, AKS, GKE, or self-managed cluster, the installation fails at those steps. Pass --gke for every existing Kubernetes cluster, whichever cloud runs it.

--openshift and --kind can't be combined. Run ./scripts/install-permit-platform.sh --help to see the flags of your package version, and compare them with the flags in this table before you script a run.

Basic installation

The shortest run on an existing Kubernetes cluster, plus the command that prints the flags of your package version:

# Existing Kubernetes cluster (GKE, EKS, AKS, or self-managed)
./scripts/install-permit-platform.sh --gke

# Get help and see all options
./scripts/install-permit-platform.sh --help

Deployment targets

One command per target. Pass exactly one of these flags:

# Existing Kubernetes cluster (GKE, EKS, AKS, or self-managed)
./scripts/install-permit-platform.sh --gke

# OpenShift (ROSA, OCP)
./scripts/install-permit-platform.sh --openshift

# Local Kind cluster (development)
./scripts/install-permit-platform.sh --kind

TLS certificate options

On an existing Kubernetes cluster, the installer expects you to provide TLS through values.yaml (see Ingress and TLS configuration) or through your infrastructure. Kind and OpenShift installations generate self-signed certificates.

# Auto-generate certificates (mkcert/OpenSSL)
./scripts/install-permit-platform.sh --gke --generate-tls

# Skip TLS configuration (external termination)
./scripts/install-permit-platform.sh --gke --skip-tls-check

# Use custom certificates (configure in values.yaml)
./scripts/install-permit-platform.sh --gke

Advanced options

# Custom namespace
./scripts/install-permit-platform.sh --gke --namespace my-namespace

# Preview without applying
./scripts/install-permit-platform.sh --gke --dry-run

# Skip image loading (REQUIRED when using pushed images to private registry)
./scripts/install-permit-platform.sh --gke --skip-images

# OpenShift with custom registry
./scripts/install-permit-platform.sh --openshift --skip-openshift-registry --skip-images

When to use the --skip-images flag

SituationUse --skip-images
You pushed the images to a private registry (Google Artifact Registry, Artifactory, Harbor, Amazon ECR, Azure ACR)Yes
You run the installer again after the images were loaded onceYes
You test Helm chart changes and don't need to reload imagesYes
First installation from the tar.gz packageNo
Kind cluster, where the installer loads images into the Docker daemonNo
Docker Hub images that aren't loaded yetNo

Push the images with scripts/push-images-to-registry.sh before you install with --skip-images. The full registry workflow is in the installation guide.

# Workflow 1: GKE with Google Artifact Registry
./scripts/push-images-to-registry.sh us-central1-docker.pkg.dev/project/permit-platform
./scripts/install-permit-platform.sh --gke --skip-images

# Workflow 2: Artifactory with imagePullSecrets
./scripts/push-images-to-registry.sh artifactory.company.com/permit-platform
kubectl create secret docker-registry registry-credentials --docker-server=artifactory.company.com ...
./scripts/install-permit-platform.sh --gke --skip-images

# Workflow 3: OpenShift, first install with the internal registry (loads the local tar files)
./scripts/install-permit-platform.sh --openshift # NO --skip-images flag

Helm releases

Three releases from one chart

The installer deploys the charts/permit-platform chart three times, as three Helm releases, in this order:

ReleaseLayerContents
third-party-servicesInfrastructurePostgreSQL, PgBouncer, Redis, OpenSearch, OpenSearch Dashboards, RabbitMQ, Keycloak
migrationsDatabase initializationThe permit-migrations job: schema setup, indices, initial data
permit-platformApplicationPermit services: backend API, frontend, Open Policy Administration Layer (OPAL) server and relays, workers, and policy sync

Manage releases individually

The installer sets extra --set flags on each release. To apply a configuration change, run the installer again, as described in Apply configuration changes.

# View all installed charts
helm list -n permit-platform

# Upgrade specific chart component
helm upgrade third-party-services charts/permit-platform \
--set permitServices.enabled=false \
--set thirdPartyServices.enabled=true \
-n permit-platform

# Check chart status
helm status permit-platform -n permit-platform
helm status third-party-services -n permit-platform
helm status migrations -n permit-platform

kubectl quick reference

Every kubectl command below uses the default namespace, permit-platform. Replace it with your namespace if you installed with --namespace. For the procedures that use these commands, with the result each one should produce, see Run daily operations.

Pods, services, and ingress

CommandWhat it shows
kubectl get pods -n permit-platformEvery pod with its ready count and status. Service pods show Running; migration job pods show Completed.
kubectl get services -n permit-platformThe services and their cluster IP addresses and ports
kubectl get deployments -n permit-platformEach deployment's ready, up-to-date, and available replica counts
kubectl get ingress -n permit-platformThe ingress, its class, its hosts, and its address

Logs

CommandWhat it shows
kubectl logs -n permit-platform deployment/permit-backend-v2Logs of the Permit API service
kubectl logs -n permit-platform deployment/permit-frontendLogs of the Permit frontend
kubectl logs -n permit-platform deployment/keycloakLogs of Keycloak, the identity provider that handles sign-in
kubectl logs -n permit-platform deployment/postgresLogs of the PostgreSQL deployment
kubectl logs -n permit-platform --selector=app --all-containers=true --prefix --tail=-1Logs of every pod that carries an app label. kubectl logs needs a pod name or a selector; without one, the command fails.

Add --previous to read the logs of a container that crashed and restarted, and -f to follow a log stream.

Restarts and scaling

CommandEffect
kubectl rollout restart deployment/permit-backend-v2 -n permit-platformRestarts one deployment
kubectl rollout restart deployment -n permit-platformRestarts every deployment in the namespace
kubectl scale deployment permit-backend-v2 -n permit-platform --replicas=3Sets the replica count of one deployment
kubectl rollout status deployment/permit-backend-v2 -n permit-platformWaits for the rollout and reports whether it succeeded
kubectl scale and kubectl patch don't survive the next installer run

The installer and helm upgrade render each deployment from values.yaml again, which resets replica counts and resource values you set with kubectl. Put a permanent change in values.yaml and run the installer again. See Scale services.

Diagnostics

CommandWhat it shows
kubectl get events -n permit-platform --sort-by='.lastTimestamp'Namespace events, oldest first, including failed image pulls and scheduling failures
kubectl describe pod <pod-name> -n permit-platformOne pod's containers, image, probes, conditions, and recent events
kubectl top pods -n permit-platform, kubectl top nodesCurrent CPU and memory usage. Both need the Kubernetes Metrics Server in the cluster.
kubectl get pv,pvc -n permit-platformPersistent volumes and claims, with their phase and capacity
kubectl get secrets -n permit-platformThe secrets in the namespace, including global-infrastructure-secret

values.yaml configuration reference

How values.yaml works

All platform settings live in charts/permit-platform/values.yaml in the installer package. The installer behaves as follows:

  • It stops with an error while global.frontendDomain is CHANGEME_FRONTEND_DOMAIN.
  • It replaces the CHANGEME_*_PASSWORD placeholders with generated passwords and saves the original file as values.yaml.backup.<timestamp>.
  • On later runs, it reuses the passwords already stored in the global-infrastructure-secret secret.

The values.yaml in your installer package is the source of truth for default values, storage sizes, and image tags. Defaults change between package versions. The blocks below show the structure and example values, not the defaults of your version. Helm ignores keys that the chart templates don't read, so compare any key you set with your package's values.yaml.

To see the images and tags running in your cluster, run kubectl get deployments -n permit-platform -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[*].image}{"\n"}{end}'.

Key sections

SectionControls
global.frontendDomainThe domain users open. Required.
global.imageRegistry, global.imagePullPolicy, global.imagePullSecretsWhere the cluster pulls images from, and the pull secrets to use. push-images-to-registry.sh sets imageRegistry.
global.postgres, global.redis, global.rabbitmqConnection settings (host, port, username, password) that Permit services use to reach the infrastructure services.
global.keycloakKeycloak admin username, admin password, and database password.
ingressIngress class, annotations, and TLS certificate.
openshiftOpenShift service account, Security Context Constraint (SCC), route termination, and security contexts.
thirdPartyServices.<service>The infrastructure deployments (postgres, pgbouncer, redis, opensearch, opensearchDashboards, rabbitmq, keycloak): enabled, image, tag, persistence.size, and resources. PostgreSQL also accepts config tuning parameters and a readReplica. OpenSearch also accepts ism and indexSettings. PgBouncer also accepts pool settings. See Connection pooling and performance tuning.
migrationsThe migrations job: enabled, image, tag, and resources
secretsWhether the release creates the platform secrets (enabled), and the backend secret's name and its LOCAL_JWT_KEY, OPAL_MASTER_TOKEN, and PDP_TESTER_TOKEN values under secrets.permitBackendSecret
permitServices.<service>The Permit deployments: enabled, replicas, image, tag, and resources.
permitServices.policySyncThe policy Git repository URL (policyRepoUrl) and SSH deploy key (sshPrivateKey).

global.postgres and thirdPartyServices.postgres configure different things. global.postgres holds the connection settings that Permit services use. thirdPartyServices.postgres configures the PostgreSQL deployment itself: image, storage, resources, and tuning.

Global configuration

If your registry needs authentication (for example, Artifactory or Harbor), create the docker-registry secret before you install, and list its name in imagePullSecrets. Google Artifact Registry on GKE, Amazon ECR on EKS, and Azure ACR on AKS authenticate through the cluster nodes and don't need a pull secret.

global:
# REQUIRED: Your platform domain
frontendDomain: "permit.yourcompany.com" # CHANGEME_FRONTEND_DOMAIN

# Image registry configuration
# This field is automatically set when using push-images-to-registry.sh script
imageRegistry: "" # Empty for Docker Hub (public images)
# Or: "us-central1-docker.pkg.dev/project/repo" for Google Artifact Registry
# Or: "gcr.io/project-id" for Google Container Registry
# Or: "123456789.dkr.ecr.region.amazonaws.com/repo" for AWS ECR
# Or: "myregistry.azurecr.io/repo" for Azure ACR
# Or: "artifactory.company.com/repo" for JFrog Artifactory
imagePullPolicy: "IfNotPresent" # Always, Never, IfNotPresent

# Note: The push-images-to-registry.sh script automatically updates this field
# when you push images to your private registry. See the installation guide, Step 4.

# Image pull secrets for private registries (optional)
# Required for registries that need authentication (Artifactory, Harbor, private registries)
# Not needed for GKE/GAR, EKS/ECR, or AKS/ACR (automatic node authentication)
#
# IMPORTANT: You must create the Kubernetes secret BEFORE installation:
# kubectl create secret docker-registry registry-credentials \
# --docker-server=artifactory.company.com \
# --docker-username=USERNAME \
# --docker-password=PASSWORD \
# --namespace=permit-platform
#
# Then reference the secret name here:
imagePullSecrets: []
# Example:
# imagePullSecrets:
# - registry-credentials
# - backup-registry-credentials

# Database configuration
postgres:
enabled: true
host: "postgres"
port: 5432
database: "permit"
username: "permit"
password: "CHANGEME_POSTGRES_PASSWORD" # Auto-generated if not set

# Redis cache configuration
redis:
enabled: true
host: "redis"
port: 6379
password: "" # Leave empty for no authentication

# Message queue configuration
rabbitmq:
enabled: true
host: "rabbitmq"
port: 5672
managementPort: 15672
username: "permit"
password: "CHANGEME_RABBITMQ_PASSWORD" # Auto-generated if not set

# Keycloak authentication
keycloak:
enabled: true
host: "keycloak"
port: 8080
adminUsername: "admin"
adminPassword: "CHANGEME_KEYCLOAK_ADMIN_PASSWORD" # Auto-generated
db:
password: "CHANGEME_KEYCLOAK_DB_PASSWORD" # Auto-generated

The chart templates read global.postgres, global.redis, and global.rabbitmq connection settings, and adminUsername, adminPassword, and db.password under global.keycloak.

OpenShift configuration

Set openshift.enabled: true for OpenShift deployments, or install with --openshift.

openshift:
enabled: false # Set to true for OpenShift deployments

# ServiceAccount for privileged operations
serviceAccount:
create: true
name: "permit-privileged-sa"
sccName: "anyuid" # Security Context Constraint

# OpenShift Route (alternative to Ingress)
route:
enabled: false # Use Ingress by default
termination: "edge" # edge, passthrough, or reencrypt
insecureEdgeTerminationPolicy: "Redirect"

# Security contexts for OpenShift
securityContext:
defaultUser: null # Let OpenShift assign
defaultGroup: null
defaultFsGroup: null

Ingress and TLS configuration

The ingress routes global.frontendDomain to the permit-frontend service. When ingress.tls.enabled is true and a certificate is set, the chart creates a TLS secret named by ingress.tls.secretName. The installer sets secretName to permit-frontend-tls.

Provide the certificate in one of two ways:

OptionKeysBehavior
Inline certificateingress.tls.certificate.cert, ingress.tls.certificate.keyThe chart writes the values into the secret's data field as they are. Kubernetes expects base64-encoded values in data.
Certificate filesingress.tls.certificateFiles.certFile, ingress.tls.certificateFiles.keyFileThe chart reads the files with Helm .Files.Get, so the paths are relative to the charts/permit-platform directory. --generate-tls sets these keys.
ingress:
enabled: true
className: "nginx" # nginx, traefik, alb, etc.
annotations: {} # Custom ingress annotations

# TLS/SSL configuration
tls:
enabled: true
# Option 1: Provide certificates directly
certificate:
cert: |
-----BEGIN CERTIFICATE-----
[Your certificate content]
-----END CERTIFICATE-----
key: |
-----BEGIN PRIVATE KEY-----
[Your private key content]
-----END PRIVATE KEY-----

# Option 2: Reference certificate files
certificateFiles:
certFile: "/path/to/cert.pem"
keyFile: "/path/to/key.pem"

Third-party services configuration

The chart builds each image reference as <global.imageRegistry>/<image>:<tag>. The example values below show one package version. Image versions, storage sizes, and resource values change between packages, so read the current values in your package's values.yaml.

The chart reads persistence.size for PostgreSQL, Redis, OpenSearch, and RabbitMQ. The chart doesn't set a storage class, so the persistent volume claims use the cluster's default storage class. The Keycloak database claim, keycloak-postgres-pvc, requests a fixed 5Gi.

Size PostgreSQL storage for your data volume. Size OpenSearch storage for your audit log volume and the retention set in ism.transitions.toDelete. OpenSearch rolls over the audit log index when the index reaches ism.rollover.minSize or ism.rollover.minAge.

thirdPartyServices:
enabled: true

# PostgreSQL database
postgres:
enabled: true
image: "postgres_15-alpine"
persistence:
enabled: true
size: "10Gi" # Minimum, 20Gi+ for production
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
# PostgreSQL tuning parameters
config:
maxConnections: "150"
sharedBuffers: "128MB"
effectiveCacheSize: "384MB"
workMem: "8MB"
maintenanceWorkMem: "64MB"
checkpointCompletionTarget: "0.9"
walBuffers: "4MB"
randomPageCost: "1.1" # SSD optimization

# Redis cache
redis:
enabled: true
image: "redis_7-alpine"
persistence:
enabled: true
size: "5Gi"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "200m"

# OpenSearch for audit logs
opensearch:
enabled: true
image: "opensearchproject_opensearch_2.11.0"
persistence:
enabled: true
size: "15Gi" # Minimum for 3 shards
resources:
requests:
memory: "3Gi"
cpu: "750m"
limits:
memory: "6Gi"
cpu: "1500m"
# Index State Management (ISM)
ism:
enabled: true
rollover:
minSize: "15gb" # Rollover at 15GB
minAge: "7d" # Or after 7 days
transitions:
toReadOnly: "7d"
toDelete: "30d" # Adjust for compliance
# Index configuration
indexSettings:
auditLogs:
numberOfShards: 3 # Default for multi-node
numberOfReplicas: 0 # No replicas for on-premise

# RabbitMQ message queue
rabbitmq:
enabled: true
image: "rabbitmq_3.12.10-management-alpine"
persistence:
enabled: true
size: "5Gi"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

# Keycloak authentication
keycloak:
enabled: true
image: "quay.io_keycloak_keycloak_20.0.5"
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"

A change to the Keycloak image version triggers a one-way Keycloak schema migration. Read the upgrade warning before you change it.

Permit services configuration

Each Permit service under permitServices accepts enabled, replicas, image, tag, and resources. The OPAL server tag below is an example. Use the tag in your package's values.yaml.

sshPrivateKey must be a multi-line block that starts with |. The chart fails to render when the key is on one line. The chart reads enabled, replicas, image, tag, resources, policyRepoUrl, and sshPrivateKey under policySync. Under backend.env, the chart reads only the authentication and frontend URL overrides (AUTH0_DOMAIN, AUTH0_ISSUER, AUTH0_JWKS_ENDPOINT, AUTH0_SSL_VERIFY, AUTH0_USERINFO_ENDPOINT, and FRONTEND_PUBLIC_URL), so other variables such as LOG_LEVEL have no effect.

permitServices:
# Backend API service
backend:
enabled: true
replicas: 1 # Scale as needed
image: "permit-backend-v2"
tag: "latest"
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"

# Frontend application
frontend:
enabled: true
replicas: 1
image: "permit-frontend"
tag: "latest"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

# OPAL policy server
opalServer:
enabled: true
replicas: 1
image: "permitio/opal-server"
tag: "0.7.5-rc.7"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

# Policy synchronization (REQUIRED)
policySync:
enabled: true
policyRepoUrl: "git@github.com:yourorg/permit-policies.git"
sshPrivateKey: |
-----BEGIN OPENSSH PRIVATE KEY-----
[Your SSH private key content]
-----END OPENSSH PRIVATE KEY-----

# Celery workers
celeryGeneral:
enabled: true
replicas: 1 # Scale based on workload
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

Keys the chart doesn't read

The values.yaml file in the installer package contains keys that the chart templates never read. Setting one has no effect, and no error tells you so. Use the alternative in the last column instead.

Key the chart ignoresWhat it looks like it doesWhat to do instead
migrations.runOnInstall, migrations.runOnUpgradeControl whether the migrations job runsNothing. The installer runs the migrations job on every install and upgrade. The chart does read migrations.enabled, migrations.image, migrations.tag, and migrations.resources.
Top-level securityContext.runAsNonRoot, securityContext.runAsUser, securityContext.fsGroupSet pod security contexts for every serviceOn OpenShift, set openshift.securityContext. See OpenShift configuration.
resourceQuotaApply a namespace resource quotaCreate a Kubernetes ResourceQuota object in the namespace yourself.
networkPolicyApply network policiesCreate Kubernetes NetworkPolicy objects in the namespace yourself.
monitoring.prometheus, monitoring.grafanaDeploy Prometheus and GrafanaDeploy your monitoring stack separately.
Top-level pgBouncerTune connection poolingSet thirdPartyServices.pgbouncer instead, with the lowercase b. See Connection pooling with PgBouncer.
autoscalingConfigure horizontal pod autoscalingSet replicas per service. See Scale services.
backup, restoreSchedule and restore backupsFollow the procedure in Back up and restore. The chart creates no scheduled backups.

Connection pooling and performance tuning

The chart has no dedicated performance-tuning section. Tune the platform through the keys it does read.

PgBouncer pools connections to PostgreSQL. The chart reads these keys under thirdPartyServices.pgbouncer:

KeyControlsChart default when unset
enabledWhether the chart deploys PgBouncerSet it in values.yaml
poolModeThe PgBouncer pool modetransaction
defaultPoolSizeServer connections per user and database pair50
maxClientConnClient connections PgBouncer accepts200
maxDbConnectionsServer connections per databaseSet it in values.yaml
ignoreStartupParametersStartup parameters PgBouncer ignoresSet it in values.yaml
image, tag, resourcesThe PgBouncer image and its CPU and memorySet them in values.yaml

Tune the rest of the platform through these keys:

To tuneSet
PostgreSQL memory, connections, and checkpointsthirdPartyServices.postgres.config. See Third-party services configuration.
A PostgreSQL read replica and its storagethirdPartyServices.postgres.readReplica: enabled, persistence, resources, and nodeAntiAffinity
CPU and memory per serviceresources under thirdPartyServices.<service> or permitServices.<service>
Throughput of a Permit servicereplicas under permitServices.<service>
Audit log index size and retentionthirdPartyServices.opensearch.ism and thirdPartyServices.opensearch.indexSettings.auditLogs

Backup and recovery configuration

The chart doesn't read backup or restore, and it creates no scheduled backups. Back up the platform with the commands in Back up and restore, and schedule them with your own job runner.

Uninstall and support

The uninstall procedure is in Uninstall Permit Platform. The support bundle procedure is in Collect a support bundle.

Next steps