Self-hosting
Configuration reference
Every self-hosted Keyright setting: the full environment-variable reference, how to generate the secrets, database setup and connection strings, white-labeling the dashboard and portal, and the Kubernetes/Helm values you will actually set.
The complete reference for configuring a self-hosted instance. The deployment guide walks you through applying these in order; this page is what you come back to for the exact name and meaning of every setting.
Environment variables
Set these as environment variables (or via a secrets manager in production).
| Variable | Required | Purpose |
|---|---|---|
KEYRIGHT_DB | yes | Npgsql connection string (see Database). Compose builds it from POSTGRES_PASSWORD for the bundled DB. |
KEYRIGHT_ADMIN_TOKEN | yes | Your vendor/admin credential, gating every /admin/* call. The customer runtime path (/v1/*) never needs it. Treat it like a root password — if unset, the whole admin surface 401s. |
KEYRIGHT_KEK | yes | Base64 32-byte key-encryption-key that encrypts your signing keys at rest. The service refuses to start without it in Production. |
KEYRIGHT_SELF_LICENSE / _FILE | yes | Your Keyright license or trial key (Licensing) — inline JSON, or a path to a file. Required to run. |
KEYRIGHT_SIGNING_KEY | recommended | Base64 PKCS#8 RSA private key that signs offline leases and air-gapped credit blocks. Your customers’ apps embed the matching public key. If unset, a per-tenant key is generated in the DB. |
KEYRIGHT_SELF_LICENSE_GRACE_DAYS | optional | Paid read-only grace window after expiry (default 15). |
KEYRIGHT_LEASE_TTL_DAYS | optional | Online-lease lifetime (default 14). |
KEYRIGHT_PUBLIC_URL | optional | External https://… origin of the dashboard/portal, so links resolve correctly behind a proxy. |
KEYRIGHT_CRON_SECRET | optional | Guards the external-scheduler endpoints (see Operations → Scheduled jobs). |
KEYRIGHT_BRAND_NAME / _COMPANY / _LOGO_URL / _COLOR / _SUPPORT_URL | optional | White-label the dashboard/portal — an Enterprise feature (applied only when your license grants whitelabel; otherwise the neutral Keyright brand). See White-label. |
KEYRIGHT_SSO_ISSUER / _AUDIENCE / _PUBLIC_KEY / _AUTHORITY | optional | Dashboard SSO (SAML/OIDC) — an Enterprise feature (active only when your license grants sso). |
KEYRIGHT_GEOIP_URL | optional | Machine-location lookup; set empty to disable for air-gapped runs. |
KEYRIGHT_PORT | optional | Host port for the bundled Compose file (default 8080). |
KEYRIGHT_PLATFORM_TOKEN | leave unset | Multi-tenant / operator mode is a licensed, managed-only capability. This token is honored only when the self-license grants an operator entitlement — which self-hosted licenses never carry — so on a self-hosted instance it has no effect: the instance is always single-tenant and stays fully license-enforced. Multi-tenancy is exclusive to Keyright’s managed (SaaS) service. |
Generate the secrets
openssl rand -hex 24 # KEYRIGHT_ADMIN_TOKEN
openssl rand -base64 32 # KEYRIGHT_KEK (decodes to 32 bytes)
openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform DER | base64 -w0 # KEYRIGHT_SIGNING_KEY
(On macOS/BSD, base64 has no -w0; use ... -outform DER | base64 | tr -d '\n'.) Your KEYRIGHT_KEK and KEYRIGHT_SIGNING_KEY are yours — you generate them, we never see them. Back them up separately and securely: without the KEK, the encrypted signing keys in your database cannot be recovered.
Database
You provide a PostgreSQL server and an empty database; Keyright builds the schema itself on first start. You never import a schema or run SQL by hand — migrations are compiled into the image and run automatically (db.Database.Migrate()), so first boot and every upgrade are a single step.
Create the database and role
CREATE DATABASE keyright ENCODING 'UTF8' TEMPLATE template0;
CREATE ROLE keyright_app LOGIN PASSWORD 'a-strong-password';
ALTER DATABASE keyright OWNER TO keyright_app; -- simplest: the app role owns its database
The app role must be able to run DDL (CREATE/ALTER/DROP), because Keyright applies EF Core migrations on startup — first boot creates every table and seeds the default tenant; upgrades apply only new migrations. Making the role the database owner is the simplest correct setup. On PostgreSQL 15+, if the role is not the owner, also GRANT ALL ON SCHEMA public TO keyright_app;.
Some managed providers (Azure Database for PostgreSQL Flexible Server, Cloud SQL) don’t let you run CREATE DATABASE directly — create the empty database and role from their console/CLI instead, then grant as above. AWS RDS/Aurora, Google Cloud SQL, Azure, Neon, Supabase, etc. all work.
Connection string (KEYRIGHT_DB)
Npgsql format; require TLS against any managed Postgres:
Host=db.internal.example.com;Port=5432;Database=keyright;Username=keyright_app;Password=...;SSL Mode=Require;Trust Server Certificate=true
Use Trust Server Certificate=true only if you don’t validate the server CA; prefer SSL Mode=VerifyFull with a trusted CA in production.
Bundled Postgres (evaluation only)
docker compose and the Helm chart (postgres.enabled=true) can start a postgres:16 for you, with the database and role pre-created and a persistent volume. Fine for evaluation and small single-node installs; for production run a managed or HA Postgres and point KEYRIGHT_DB at it.
White-label the dashboard & portal (Enterprise)
Set the brand variables and restart. They apply only when your license grants whitelabel (Enterprise); on Standard or a lapsed license the neutral Keyright brand is used and these are ignored. There is no dashboard “Settings” toggle for this on self-host — branding is configuration, not a stored setting.
KEYRIGHT_BRAND_NAME=Acme Licensing # replaces "Keyright" in the dashboard/portal + emails
KEYRIGHT_BRAND_COMPANY=Acme GmbH # footer / legal line
KEYRIGHT_BRAND_LOGO_URL=https://cdn.acme.example/logo.svg # absolute https URL to a logo (SVG/PNG); omit for the default mark
KEYRIGHT_BRAND_COLOR=#2f63f6 # accent colour as a hex #RRGGBB
KEYRIGHT_BRAND_SUPPORT_URL=https://acme.example/support # "Support" link target
Confirm it applied with GET <base>/branding (returns the live name/company/logo/color).
Kubernetes (Helm) values
The chart in deploy/helm/keyright/ reads these. The deployment guide has the full helm upgrade --install commands; the knobs you’ll most often set:
| Value | Purpose |
|---|---|
image.repository / image.tag | Image + version. Unset image.tag defaults to the chart’s appVersion; pin it (e.g. --set image.tag=2.2.2) for reproducible deploys. |
secrets.adminToken / secrets.kek | Required — the chart refuses to render without them. Pass with --set-string. |
secrets.selfLicense | Your license. Pass with --set-file secrets.selfLicense=license.json — never --set-string (JSON commas break the parser). |
postgres.enabled | true (default) runs a bundled in-cluster Postgres for evaluation; set false for production and supply externalDatabase.connectionString. |
postgres.image | Override the bundled DB image, e.g. --set postgres.image=<registry>/postgres:16 to avoid Docker Hub pull limits or in a locked-down cluster. |
externalDatabase.connectionString | Your KEYRIGHT_DB when postgres.enabled=false. Pass with --set-string (contains commas). |
replicaCount | Number of stateless app nodes (2+ for HA — no session affinity needed). |
brand.name / .company / .color / .logoUrl / .supportUrl | White-label (Enterprise). Pass with --set-string. |
ingress.*, resources.* | Ingress host/TLS and per-pod CPU/memory. See values.yaml for every knob. |
The app runs its database migrations at startup, so the database must be reachable when the pod starts — an unreachable DB (bad host/credentials, no network path, missing firewall rule) makes the pod CrashLoopBackOff with an Npgsql “connection” error, not an un-ready pod. Confirm DNS/egress to your DB before deploying. GET /health is liveness; GET /health/ready also checks DB connectivity once running.
Reach the service without an ingress via a port-forward. The service is named <release>-keyright — with release keyright that resolves to keyright-keyright:
kubectl port-forward -n keyright svc/keyright-keyright 8080:8080