Installation Reference — Helm & Custom Deployment
Audience: DevOps engineers, platform engineers, SREs Time: ~45–90 min (first production deployment)
New to Control Core? Start with Quick deploy first — it walks you through the complete flow in 60 minutes using Docker Compose. Return to this page when you are ready for a production Helm deployment or need a detailed configuration reference.
This guide serves as the full configuration reference for deploying Control Core in production environments — Kubernetes with Helm, bare-metal, or cloud VMs. It covers all required and optional configuration values, TLS setup, secret management patterns, and validation steps.
Where this fits in the deployment journey
Quick deploy (Docker Compose, ~60 min) ← start here for first evaluation
↓
Kickstart guide (full Docker Compose ops) ← detailed Compose reference
↓
This guide (Helm / production reference) ← you are here
↓
Enterprise guide (HA, multi-region, scaling)
Preflight checklist
Before beginning a production deployment, confirm:
- Kubernetes 1.24+ cluster (or Docker Engine 20.10+ for Compose) with sufficient resources
- Helm 3.10+ installed on the operator workstation
- PostgreSQL 13+ (managed service recommended — AWS RDS, Azure Database, GCP Cloud SQL)
- Redis 6+ (managed service recommended — AWS ElastiCache, Azure Cache for Redis)
- DNS entries provisioned for Control Plane and protected resource domains
- TLS certificate strategy decided (ingress-controller-managed or customer-provided cert/key)
- Private GitHub (or GitLab) repository created for your controls, with a personal access token
- Secrets manager available (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or Kubernetes Secrets)
- Control Core deployment package received from
info@controlcore.io
Troubleshooting: If you have not yet received your deployment package, contact
info@controlcore.iowith your company name and tier (Kickstart or Custom). Packages are provisioned within 1 business day.
Helm deployment
Add the Helm repository
helm repo add controlcore https://charts.controlcore.io
helm repo update
Review and configure values
Download the default values file to understand all configurable options:
helm show values controlcore/control-core > my-values.yaml
Edit my-values.yaml for your environment. The sections below describe the required and recommended values.
Control Plane values
controlPlane:
# Public URL (HTTPS) — used for OIDC callbacks and API client configuration
publicUrl: "https://controlplane.yourcompany.com"
# Image (provided by Control Core — do not change tag unless instructed)
image:
repository: "registry.controlcore.io/control-plane-api"
tag: "latest" # pin to a specific version for production
pullPolicy: IfNotPresent
replicaCount: 2 # minimum 2 for production HA
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2000m"
memory: "2Gi"
# All secrets injected from Kubernetes Secret or external secret manager
secretRef: "controlcore-secrets" # name of a pre-created K8s Secret
Database values
database:
# Use external managed PostgreSQL — do not use the bundled dev DB in production
external: true
host: "your-db-host.rds.amazonaws.com"
port: 5432
name: "controlcore"
user: "ccuser"
# Password sourced from secretRef — never inline in values
passwordSecretKey: "DATABASE_PASSWORD"
Redis values
redis:
external: true
host: "your-redis.cache.amazonaws.com"
port: 6379
passwordSecretKey: "REDIS_PASSWORD"
Controls repository (GitHub)
controlsRepo:
url: "https://github.com/your-org/your-controls-repo"
branch: "main"
pollingIntervalSeconds: 30
# Token sourced from secretRef
tokenSecretKey: "GITHUB_TOKEN"
Bouncer values
Each Bouncer is deployed as a separate Helm release (or as a values override per environment):
bouncer:
id: "bouncer-prod-api-01"
name: "Production API Bouncer"
type: "reverse-proxy" # reverse-proxy | sidecar
environment: "production"
# Control Plane connection
controlPlaneUrl: "https://controlplane.yourcompany.com"
apiKeySecretKey: "BOUNCER_API_KEY"
# Resource being protected
resourceName: "Customer API"
resourceType: "api" # api | webapp | database | ai-agent | mcp-server
targetHost: "customer-api-svc:8080"
originalHostUrl: "https://api.yourcompany.com"
# Fail-closed security posture
securityPosture: "deny-all"
replicaCount: 2
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
Troubleshooting: Bouncer pods stay in
CrashLoopBackOff?
kubectl logs deploy/controlcore-bouncer— look forPAP_API_URL connection refusedorAPI_KEY invalid- Confirm the Control Plane is healthy before deploying Bouncers
- Confirm
BOUNCER_API_KEYin the K8s secret matches the environment API key in Settings → Environments
Ingress values
ingress:
enabled: true
className: "nginx" # or alb, traefik, etc.
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: "controlplane.yourcompany.com"
paths:
- path: "/"
pathType: Prefix
tls:
- secretName: "controlcore-tls"
hosts:
- "controlplane.yourcompany.com"
Deploy
# Create namespace
kubectl create namespace controlcore
# Create secrets (or use external-secrets operator / Vault agent)
kubectl create secret generic controlcore-secrets \
--namespace controlcore \
--from-literal=SECRET_KEY=$(openssl rand -hex 32) \
--from-literal=JWT_SECRET_KEY=$(openssl rand -hex 32) \
--from-literal=DATABASE_PASSWORD='<your-db-password>' \
--from-literal=REDIS_PASSWORD='<your-redis-password>' \
--from-literal=GITHUB_TOKEN='<your-github-pat>' \
--from-literal=BOUNCER_API_KEY='<your-api-key>' \
--from-literal=CC_BUILTIN_ADMIN_PASS='<your-admin-password>'
# Deploy Control Plane
helm install controlcore controlcore/control-core \
--namespace controlcore \
--values my-values.yaml \
--wait
# Deploy Bouncer (separate release)
helm install controlcore-bouncer controlcore/bouncer \
--namespace controlcore \
--values my-bouncer-values.yaml \
--wait
Troubleshooting: Helm install times out waiting for pods?
kubectl get pods -n controlcore— check pod statuskubectl describe pod <pod-name> -n controlcore— look for image pull errors or resource limits- Confirm registry credentials are configured:
kubectl get secret controlcore-registry -n controlcore
Required configuration reference
Control Plane
| Variable | Required | Description |
|---|---|---|
CONTROL_PLANE_PUBLIC_URL | Yes | HTTPS URL of the Control Plane UI (used for OIDC callbacks) |
DATABASE_URL | Yes | PostgreSQL connection string |
REDIS_URL | Yes | Redis connection string |
SECRET_KEY | Yes | 32-char random — used for internal encryption |
JWT_SECRET_KEY | Yes | 32-char random — used for JWT signing |
CC_BUILTIN_ADMIN_PASS | Yes | Initial admin password (change after first login) |
GITHUB_TOKEN | Yes | Personal access token with repo scope for controls repository |
POLICY_REPO_URL | Yes | HTTPS URL of your controls GitHub repository |
POLICY_REPO_BRANCH | No | Branch to sync (default: main) |
LOG_LEVEL | No | INFO (default) · DEBUG · WARNING · ERROR |
CORS_ALLOWED_ORIGINS | No | Comma-separated list of allowed origins for the UI |
OIDC_ENABLED | No | false (default) — set to true to enable OIDC SSO |
OIDC_PROVIDER_URL | Conditional | Required when OIDC_ENABLED=true |
Bouncer
| Variable | Required | Description |
|---|---|---|
BOUNCER_ID | Yes | Unique identifier for this Bouncer instance |
BOUNCER_NAME | Yes | Human-readable label (shown in Console) |
BOUNCER_TYPE | Yes | reverse-proxy or sidecar |
ENVIRONMENT | Yes | sandbox or production |
PAP_API_URL | Yes | Internal URL of the Control Plane API |
API_KEY | Yes | Environment API key from Settings → Environments |
RESOURCE_NAME | Yes | Human-readable resource name (used to pair Sandbox + Production) |
RESOURCE_TYPE | Yes | api · webapp · database · ai-agent · mcp-server |
TARGET_HOST | Yes | Internal host:port of the protected resource |
ORIGINAL_HOST_URL | Yes | External URL your clients use to reach the resource |
SECURITY_POSTURE | No | deny-all (default and recommended) |
BOUNCER_ENABLE_POST_DECISION_ACTIONS | No | true (default) — set to false to disable action dispatch |
Domain and TLS
- Set
CONTROL_PLANE_PUBLIC_URLto a valid HTTPS domain (not an IP) — this is used for OIDC redirect URIs and API client configuration - TLS must be terminated at or before the ingress — do not run the Control Plane API on plaintext HTTP in production
- For protected resources, set
ORIGINAL_HOST_URLto the external HTTPS URL clients use, andTARGET_HOSTto the internal address (e.g.my-service:8080) — never expose internal addresses externally
Security requirements
- TLS 1.2+ on all external and cross-zone traffic
- No plaintext secrets in version control — use a secrets manager or Kubernetes Secrets
- Database and Redis endpoints restricted to private networks (no public exposure)
- Separate API keys per environment (Sandbox and Production never share a key)
- Schedule key and secret rotation (recommended: every 90 days minimum)
Validation checklist
Run these checks after deployment to confirm a healthy installation:
# Control Plane health
curl -s https://controlplane.yourcompany.com/api/v1/health | jq .
# Expected: {"status": "ready", "database": "connected", "redis": "connected"}
# Bouncer health
curl -s https://api.yourcompany.com/health | jq .
# Expected: {"status": "ready", "policy-bridge": "connected"}
Then in the Console:
- Login works with admin credentials
- Settings → PEPs — Bouncer appears as Active
- Settings → Resources — resource auto-registered
- Settings → Controls Repository — GitHub sync status is healthy
- Create a test control → deploy to Sandbox → send a request → audit decision visible
Troubleshooting: Any check failing? See Admin Troubleshooting and Troubleshooting.
Next steps
- Kickstart guide — full Docker Compose operations reference
- Enterprise deployment (Kubernetes HA) — multi-replica, HA patterns
- Controls Manager Guide — start authoring your first controls
- Administrator Guide — post-deployment configuration and ongoing operations