On-Premises Reference
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.
| Flag | Effect |
|---|---|
--gke | Deploy 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. |
--openshift | Deploy to an OpenShift cluster, such as Red Hat OpenShift Service on AWS (ROSA) or OpenShift Container Platform (OCP). |
--kind | Create 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 NAMESPACE | Install into NAMESPACE. Default: permit-platform. |
--generate-tls | Generate TLS certificates. |
--skip-tls-check | Skip the TLS check. Use it when TLS terminates outside the cluster ingress, for example at a cloud load balancer or with cert-manager. |
--skip-images | Don't load images from the package. Required when the images are already in your private registry. |
--skip-openshift-registry | On OpenShift, don't configure the internal OpenShift registry. Use it with a custom registry. |
--dry-run | Show what the installer would deploy without applying it. |
--yes | Confirm a Keycloak schema migration without a prompt. Required for non-interactive runs when the Keycloak version changes. |
-h, --help | Print the installer help. |
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
| Situation | Use --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 once | Yes |
| You test Helm chart changes and don't need to reload images | Yes |
First installation from the tar.gz package | No |
| Kind cluster, where the installer loads images into the Docker daemon | No |
| Docker Hub images that aren't loaded yet | No |
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:
| Release | Layer | Contents |
|---|---|---|
third-party-services | Infrastructure | PostgreSQL, PgBouncer, Redis, OpenSearch, OpenSearch Dashboards, RabbitMQ, Keycloak |
migrations | Database initialization | The permit-migrations job: schema setup, indices, initial data |
permit-platform | Application | Permit 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
| Command | What it shows |
|---|---|
kubectl get pods -n permit-platform | Every pod with its ready count and status. Service pods show Running; migration job pods show Completed. |
kubectl get services -n permit-platform | The services and their cluster IP addresses and ports |
kubectl get deployments -n permit-platform | Each deployment's ready, up-to-date, and available replica counts |
kubectl get ingress -n permit-platform | The ingress, its class, its hosts, and its address |
Logs
| Command | What it shows |
|---|---|
kubectl logs -n permit-platform deployment/permit-backend-v2 | Logs of the Permit API service |
kubectl logs -n permit-platform deployment/permit-frontend | Logs of the Permit frontend |
kubectl logs -n permit-platform deployment/keycloak | Logs of Keycloak, the identity provider that handles sign-in |
kubectl logs -n permit-platform deployment/postgres | Logs of the PostgreSQL deployment |
kubectl logs -n permit-platform --selector=app --all-containers=true --prefix --tail=-1 | Logs 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
| Command | Effect |
|---|---|
kubectl rollout restart deployment/permit-backend-v2 -n permit-platform | Restarts one deployment |
kubectl rollout restart deployment -n permit-platform | Restarts every deployment in the namespace |
kubectl scale deployment permit-backend-v2 -n permit-platform --replicas=3 | Sets the replica count of one deployment |
kubectl rollout status deployment/permit-backend-v2 -n permit-platform | Waits for the rollout and reports whether it succeeded |
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
| Command | What 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-platform | One pod's containers, image, probes, conditions, and recent events |
kubectl top pods -n permit-platform, kubectl top nodes | Current CPU and memory usage. Both need the Kubernetes Metrics Server in the cluster. |
kubectl get pv,pvc -n permit-platform | Persistent volumes and claims, with their phase and capacity |
kubectl get secrets -n permit-platform | The 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.frontendDomainisCHANGEME_FRONTEND_DOMAIN. - It replaces the
CHANGEME_*_PASSWORDplaceholders with generated passwords and saves the original file asvalues.yaml.backup.<timestamp>. - On later runs, it reuses the passwords already stored in the
global-infrastructure-secretsecret.
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
| Section | Controls |
|---|---|
global.frontendDomain | The domain users open. Required. |
global.imageRegistry, global.imagePullPolicy, global.imagePullSecrets | Where the cluster pulls images from, and the pull secrets to use. push-images-to-registry.sh sets imageRegistry. |
global.postgres, global.redis, global.rabbitmq | Connection settings (host, port, username, password) that Permit services use to reach the infrastructure services. |
global.keycloak | Keycloak admin username, admin password, and database password. |
ingress | Ingress class, annotations, and TLS certificate. |
openshift | OpenShift 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. |
migrations | The migrations job: enabled, image, tag, and resources |
secrets | Whether 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.policySync | The 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:
| Option | Keys | Behavior |
|---|---|---|
| Inline certificate | ingress.tls.certificate.cert, ingress.tls.certificate.key | The chart writes the values into the secret's data field as they are. Kubernetes expects base64-encoded values in data. |
| Certificate files | ingress.tls.certificateFiles.certFile, ingress.tls.certificateFiles.keyFile | The 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 ignores | What it looks like it does | What to do instead |
|---|---|---|
migrations.runOnInstall, migrations.runOnUpgrade | Control whether the migrations job runs | Nothing. 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.fsGroup | Set pod security contexts for every service | On OpenShift, set openshift.securityContext. See OpenShift configuration. |
resourceQuota | Apply a namespace resource quota | Create a Kubernetes ResourceQuota object in the namespace yourself. |
networkPolicy | Apply network policies | Create Kubernetes NetworkPolicy objects in the namespace yourself. |
monitoring.prometheus, monitoring.grafana | Deploy Prometheus and Grafana | Deploy your monitoring stack separately. |
Top-level pgBouncer | Tune connection pooling | Set thirdPartyServices.pgbouncer instead, with the lowercase b. See Connection pooling with PgBouncer. |
autoscaling | Configure horizontal pod autoscaling | Set replicas per service. See Scale services. |
backup, restore | Schedule and restore backups | Follow 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:
| Key | Controls | Chart default when unset |
|---|---|---|
enabled | Whether the chart deploys PgBouncer | Set it in values.yaml |
poolMode | The PgBouncer pool mode | transaction |
defaultPoolSize | Server connections per user and database pair | 50 |
maxClientConn | Client connections PgBouncer accepts | 200 |
maxDbConnections | Server connections per database | Set it in values.yaml |
ignoreStartupParameters | Startup parameters PgBouncer ignores | Set it in values.yaml |
image, tag, resources | The PgBouncer image and its CPU and memory | Set them in values.yaml |
Tune the rest of the platform through these keys:
| To tune | Set |
|---|---|
| PostgreSQL memory, connections, and checkpoints | thirdPartyServices.postgres.config. See Third-party services configuration. |
| A PostgreSQL read replica and its storage | thirdPartyServices.postgres.readReplica: enabled, persistence, resources, and nodeAntiAffinity |
| CPU and memory per service | resources under thirdPartyServices.<service> or permitServices.<service> |
| Throughput of a Permit service | replicas under permitServices.<service> |
| Audit log index size and retention | thirdPartyServices.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.