Deployment Guide
Overview
Kinotic OS deploys to Kubernetes and provides several deployment configurations for different environments.
Deployment Options
Helm Charts
Production-ready Helm charts are available in deployment/helm/ for deploying:
- Kinotic servers —
deployment/helm/kinotic: the org, system and app servers in one release, each a Deployment, Service and ConfigMap of its own with its replicas, resources, service type and TLS certificate, and the migration Job that precedes them. Every server's pods join one headless Service, through which Ignite discovers its peers, so the three form one cluster, and each server signs tokens with a JWT key set of its own,<server>-jwt-signing-keys - Elasticsearch — Search and persistence cluster
- Load Generator — For performance testing
Each server mounts a platform-secrets volume, projected from the global Key Vault. It holds the server's JWT key set and the secrets the server imports as properties when it starts, each file named after the property it sets:
| File | Key Vault secret | Mounted on |
|---|---|---|
jwt-signing-keys | <server>-jwt-signing-keys | each server, its own key set |
kinotic.domain.secretStorage.masterKey | kinotic-secret-storage-master-key | every server |
kinotic.managementApi.github.appPrivateKey | kinotic-github-app-private-key | the servers with githubApp set: the org and system servers, which load management-api |
kinotic.managementApi.github.webhookSecret | kinotic-github-webhook-secret | the same |
kinotic.domain.publishedSites.cloudflare.apiToken | kinotic-cloudflare-dns-api-token | the servers with cloudflareDns set: the management and system servers, which create an organization's sites record |
kinotic.managementServer.turnstile.secretKey | kinotic-turnstile-secret-key | the server with turnstile set: the management server, which verifies the find-my-organizations challenge |
kinotic.domain.secretStorage.backend selects where SecretStorageService persists secrets.
IN_MEMORY, the default, keeps them in process memory, so a restart loses every stored secret. AZURE stores them in
the Key Vault at kinotic.domain.secretStorage.azure.vaultUrl; that backend is an Enterprise
Feature in the kinotic-ee module, which the org, system and app servers include, and its
production use needs a Kinotic Enterprise License agreement with Minds Ignited (see
Enterprise License). A server
configured with a backend whose module is not on its classpath fails at startup.
A server picks up a rotated JWT key set while it runs, and an imported secret when it restarts.
On Azure the global terraform seeds the master key and takes the GitHub App's key and webhook
secret from TF_VAR_github_app_private_key and TF_VAR_github_webhook_secret.
Docker Compose (Local Development)
A Docker Compose configuration in deployment/docker-compose/ provides a complete local development environment including the org, system and app servers (compose.kinotic-servers.yml), Elasticsearch, and supporting services. This is the recommended way to run Kinotic OS during development.
Development Server
A single-host development environment on Proxmox — a container per service, the three servers behind an edge that routes the one forwarded port by SNI, three Elasticsearch nodes on their own disks, and Cloud Hypervisor nodes on machines of their own — that keeps the sites account and email in Azure, the sites on Cloudflare, and is designed to migrate its organizations to Kinotic Cloud. See Development Server.
Cloud Providers (Terraform)
Terraform configurations in deployment/terraform/ support deployment to:
- AWS — EC2-based deployment with configurable instance types
- Azure — VM-based deployment on Azure infrastructure
UI sites on Cloudflare
Published UIs live on the customer domain, a Cloudflare zone (kinotic.io) separate from
the platform's own, so that no customer content is ever under the domain the portal's cookies
live on. sites.tf calls deployment/terraform/azure/modules/sites for the sites storage
account st<prefix>sites every site's files live in under sites/<hostname>/, private, with
a read-only SAS on its container minted for the edge; cloudflare.tf calls modules/cloudflare-sites for the edge,
once per environment: the zone's settings (Full (strict), HTTPS only, TLS 1.2) and Total TLS,
the proxied records apps-api.<zone> and *.apps-api.<zone> to the app server's load
balancer, and the sites Worker (deployment/cloudflare/sites-worker) bound to *.<zone>/*,
with the apps-api hosts bound to no Worker so API calls and WebSockets reach the app server
directly. A site is <app>--<ui>.<org>.<zone>; the Worker reads it from the account and
serves a route of the single-page application its index. Nothing is created per application,
UI or tenant.
Per organization, two proxied records are created, and the platform creates them: the wildcard
*.<org>.<zone> for its sites and <org>.<zone> for its own host, at sign-up (the management server)
or before the organization's first publish (the system server), through CloudflareOrganizationDnsRecordProvisioner with a token holding
DNS edit on the zone and nothing else, which the global root stores in the platform vault as
kinotic-cloudflare-dns-api-token. Total TLS issues the organization's certificate from the
record within minutes, and <org>.<zone> itself, covered by the zone's universal
certificate, redirects to the portal's login for the organization. The app server's names in
the zone are on the cluster certificate: cert-manager answers their DNS-01 challenges through
Cloudflare with the same token (tls.tf), so Full (strict) validates the origin.
The portal's find-my-organizations form presents a Cloudflare Turnstile challenge, and the
management server verifies each request's token: the global root stores the site's secret key in the
platform vault as kinotic-turnstile-secret-key (TF_VAR_turnstile_secret_key), and the cluster
root passes the site key as turnstile_site_key, which Helm hands the management server as
kinotic.managementServer.turnstile.siteKey. The site is created in the Cloudflare dashboard under
Turnstile, for the portal's hostname.
kinotic.tf passes the zone as the sites domain and the account's blob endpoint to the system
server as KINOTIC_SYSTEMAPI_UIDEPLOYMENT_*, the app server's apps-api.<zone> base, and the
KINOTIC_DOMAIN_PUBLISHEDSITES_* settings every server provisions organizations with (see
Configuration). The cluster root takes the zone's id, the
account id and two tokens as variables: the operator's, which terraform applies the zone and
the Worker with, and the DNS token. The system server's development profile disables the
provisioner instead.
A developer machine gets the same modules from deployment/terraform/azure/dev, under
<environment>.<zone>. The contributing guide
walks through it.