Back to Copilotkit

Self-Hosting Intelligence

showcase/shell-docs/src/content/docs/premium/self-hosting.mdx

1.57.017.6 KB
Original Source

What is this?

CopilotKit Intelligence — the platform that powers threads, shared state, the inspector, and observability — can be self-hosted on your own Kubernetes cluster using the copilot-intelligence Helm chart. Self-hosting is a licensed deployment mode: you run the control plane and data plane inside your own network boundary, bring your own Postgres and Redis (or run bundled Bitnami subcharts), point the chart at your own OIDC provider, and manage secrets with External Secrets Operator or direct Kubernetes Secrets. The chart deploys three core workloads — app-api (the backend service, port 4201), app-frontend (the web UI, port 8080), and an optional realtime-gateway (a WebSocket service for realtime sync, port 4401) — plus supporting Services, Ingress, HPAs, PodDisruptionBudgets, and ExternalSecret resources.

When should I use this?

  • Your organization requires CopilotKit Intelligence to run inside your own VPC or data center for compliance, data residency, or security reasons
  • You want to connect Intelligence to internal databases, identity providers, or secret stores that are not reachable from Copilot Cloud
  • You need to operate the platform under your existing Kubernetes tooling, CI/CD, and observability stack
  • You have a CopilotKit Intelligence Platform license and the platform-engineering capacity to run a production Kubernetes workload

If none of these apply, use Copilot Cloud — it is the fastest path to a working Intelligence deployment and requires no cluster operations.

Prerequisites

Before starting, make sure the following are in place. The How the Intelligence Platform Works page explains the layering in more depth.

License and registry access:

  • A valid CopilotKit Intelligence Platform license key (contact your CopilotKit account team if you do not have one)
  • Read access to the chart OCI registry at oci://ghcr.io/copilotkit/charts/copilot-intelligence

Cluster and tooling:

  • Kubernetes ≥ 1.28
  • Helm ≥ 3.12
  • kubectl configured against the target cluster with an admin-equivalent context

Platform prerequisites (cluster-wide, installed once):

  • An ingress controller — either nginx-ingress or the AWS Load Balancer Controller
  • cert-manager (or a cloud-managed certificate alternative such as AWS ACM) for TLS on the public hostnames
  • External Secrets Operator if you plan to sync secrets from AWS Secrets Manager, HashiCorp Vault, or GCP Secret Manager (strongly recommended for production)

External dependencies (reachable from the cluster):

  • PostgreSQL ≥ 14 — managed (Amazon RDS, Aurora, Cloud SQL) or operator-deployed in-cluster
  • Redis ≥ 7 (or a Valkey-compatible service such as Amazon ElastiCache)
  • An OIDC identity provider — Keycloak, Okta, Azure AD, Auth0, Google Workspace, or equivalent

Optional:

  • Amazon OpenSearch (only when analytics features are in use)
  • An S3-compatible object store (only when the realtime gateway is configured to persist AG-UI events)

Implementation

<Steps> <Step> ### Prepare your Kubernetes cluster
Ensure `kubectl` points to the cluster that will run Intelligence.

```bash title="Terminal"
kubectl config current-context
kubectl auth can-i create namespace --all-namespaces
```

The context shown should be the target cluster, and the permission check should return `yes`. If either is wrong, fix your kubeconfig before proceeding.
</Step> <Step> ### Install platform prerequisites
These components are cluster-wide and installed once per cluster, independently of the application chart.

<Tabs items={["AWS (EKS)", "On-prem / generic"]}>
  <Tab value="AWS (EKS)">
    ```bash title="Terminal"
    # AWS Load Balancer Controller (kube-system)
    helm repo add eks https://aws.github.io/eks-charts
    helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
      -n kube-system \
      --set clusterName=<YOUR_CLUSTER_NAME>

    # cert-manager
    helm repo add jetstack https://charts.jetstack.io
    helm install cert-manager jetstack/cert-manager \
      -n cert-manager --create-namespace \
      --set installCRDs=true

    # External Secrets Operator
    helm repo add external-secrets https://charts.external-secrets.io
    helm install external-secrets external-secrets/external-secrets \
      -n external-secrets --create-namespace
    ```
  </Tab>
  <Tab value="On-prem / generic">
    ```bash title="Terminal"
    # NGINX Ingress Controller
    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm install ingress-nginx ingress-nginx/ingress-nginx \
      -n ingress-nginx --create-namespace

    # cert-manager
    helm repo add jetstack https://charts.jetstack.io
    helm install cert-manager jetstack/cert-manager \
      -n cert-manager --create-namespace \
      --set installCRDs=true
    ```
  </Tab>
</Tabs>

After each controller is running, its pods should be `Ready` in their respective namespaces.
</Step> <Step> ### Provision external dependencies
Intelligence needs Postgres, Redis, and an OIDC issuer. You can either point the chart at managed services you already run, or enable the bundled Bitnami subcharts for in-cluster Postgres and Redis (appropriate for evaluation and small self-hosted installs).

**Using managed services (recommended for production):**

- Create a Postgres database and user. Record the host, port (default `5432`), database name, username, and password.
- Create a Redis instance with TLS enabled. Record the host, port (default `6379`), and password.
- Configure an OIDC client in your identity provider. Record the issuer URL, client ID, and client secret.

**Using the bundled in-cluster subcharts:**

Set `postgresql.enabled: true` and `redis-subchart.enabled: true` in your values file (covered in the next step). A matching `StorageClass` must exist in the cluster. The bundled Keycloak subchart is available via `keycloak.enabled: true` if you also need a quick OIDC provider for evaluation; do not use the bundled Keycloak for production workloads.
</Step> <Step> ### Create a values file
The chart ships with example values files for the two most common shapes. Pull the chart, then copy the example closest to your environment.

```bash title="Terminal"
helm pull oci://ghcr.io/copilotkit/charts/copilot-intelligence --version 0.1.0 --untar

# AWS-flavored (ALB, IRSA, External Secrets from AWS Secrets Manager)
cp copilot-intelligence/values-aws-example.yaml my-values.yaml

# Or on-prem-flavored (nginx, manual Kubernetes Secrets)
cp copilot-intelligence/values-onprem-example.yaml my-values.yaml
```

Edit `my-values.yaml` to set at minimum:

- `database.host`, `database.port`, `database.name` — your Postgres connection
- `redis.host`, `redis.port`, `redis.tls` — your Redis connection
- `auth.issuer` — your OIDC provider's issuer URL
- `auth.existingSecret` — name of the Kubernetes Secret containing `auth-secret`, `auth-client-id`, `auth-client-secret`
- `ingress.ui.host` — the hostname users will load the Intelligence UI on (for example `intelligence.example.com`)
- `ingress.api.host` — an optional dedicated API hostname (defaults to `ingress.ui.host`)
- `ingress.tls` — TLS configuration for the hosts above

See the [Configuration reference](#configuration-reference) section for the full set of values.
</Step> <Step> ### Create secrets
You have two paths — External Secrets Operator (recommended) or direct Kubernetes Secrets.

**External Secrets Operator:**

1. Ensure your secret backend (AWS Secrets Manager, Vault, etc.) has entries for the database URL, Redis URL, and auth credentials.
2. Create a `ClusterSecretStore` (or `SecretStore`) that references that backend.
3. In `my-values.yaml`, set `externalSecrets.enabled: true`, `externalSecrets.store.kind`, and `externalSecrets.store.name` to match. The chart generates `ExternalSecret` resources that sync those entries into Kubernetes Secrets at the names `app-api` expects.

**Direct Kubernetes Secrets:**

Set `externalSecrets.enabled: false` in your values file, then create the Secrets manually:

```bash title="Terminal"
kubectl create namespace copilot-intelligence

kubectl create secret generic cpki-db \
  --from-literal=database-url='postgresql://user:pass@host:5432/intelligence' \
  -n copilot-intelligence

kubectl create secret generic cpki-redis \
  --from-literal=redis-url='rediss://:password@host:6379' \
  -n copilot-intelligence

kubectl create secret generic cpki-auth \
  --from-literal=auth-secret='<32+ character random string>' \
  --from-literal=auth-client-id='<OIDC client id>' \
  --from-literal=auth-client-secret='<OIDC client secret>' \
  -n copilot-intelligence
```

The exact Secret names referenced in your values file must match whatever you create.
</Step> <Step> ### (Optional) Enable schema migrations
The chart can run database schema migrations as a pre-install `Job`. This is **disabled by default** (`migrations.enabled: false`). If you want the chart to apply migrations for you on install, set the following in `my-values.yaml`:

```yaml title="my-values.yaml"
migrations:
  enabled: true
```

With this enabled, `helm install` blocks the rollout until the migrations Job reports `Completed`. Leave it disabled if you manage schema migrations out-of-band (for example, via your existing CI/CD or DBA pipeline).
</Step> <Step> ### Install the chart
```bash title="Terminal"
helm install copilot-intelligence ./copilot-intelligence \
  -f my-values.yaml \
  -n copilot-intelligence \
  --create-namespace \
  --wait \
  --timeout 10m
```

`--wait` blocks until the `Deployments` report healthy replicas; `--timeout 10m` allows enough time for image pulls and (if you enabled it in the previous step) the initial database migration job.
</Step> <Step> ### Verify the install
Check that every pod is `Running` and the ingress is ready:

```bash title="Terminal"
kubectl get pods -n copilot-intelligence
kubectl get ingress -n copilot-intelligence
```

You should see `app-api`, `app-frontend`, and — if enabled — `realtime-gateway` pods running. If you opted into migrations (`migrations.enabled: true`, see the values step above), the migrations `Job` will also appear as `Completed`; if you left migrations disabled, no Job is created.

Confirm the API health check reports `ok`:

```bash title="Terminal"
curl https://<ingress.api.host>/api/health
```

The endpoint returns `200 OK` only when the database is reachable — a failed health check is almost always a database connectivity problem.

Finally, browse to `https://<ingress.ui.host>` and log in via your OIDC provider. A successful login confirms end-to-end wiring.
</Step> <Step> ### Upgrade and uninstall
**Upgrade** — bump the chart version in your `helm pull` command, regenerate example values to diff against, then run:

```bash title="Terminal"
helm upgrade copilot-intelligence ./copilot-intelligence \
  -f my-values.yaml \
  -n copilot-intelligence \
  --wait
```

**Uninstall** — releases leave PersistentVolumes in place by default if you enabled bundled subcharts; delete them manually if you intend to tear down state.

```bash title="Terminal"
helm uninstall copilot-intelligence -n copilot-intelligence
```
</Step> </Steps>

Configuration reference

The tables below summarize the most common values. For every option, see values.yaml in the pulled chart.

Global

KeyDescriptionDefault
global.imageRegistryRegistry prefix for unqualified image names""
global.imagePullSecretsImage pull secrets for private registries[]
global.storageClassStorageClass override for bundled subcharts""

Database

KeyDescriptionDefault
database.hostPostgres host"" (required)
database.portPostgres port5432
database.nameDatabase nameintelligence
database.existingSecretPre-existing Secret with database-url""

Redis

KeyDescriptionDefault
redis.hostRedis host"" (required)
redis.portRedis port6379
redis.tlsRequire TLS (ElastiCache defaults to on)true
redis.existingSecretPre-existing Secret with redis-url""

Authentication

KeyDescriptionDefault
auth.deploymentModeself-hosted or hostedself-hosted
auth.issuerOIDC issuer URL"" (required)
auth.existingSecretSecret with auth-secret, auth-client-id, auth-client-secret""
auth.defaultOrganizationIdDefault organization ID in self-hosted modedefault

Ingress

KeyDescriptionDefault
ingress.enabledCreate Ingress resourcestrue
ingress.classNamenginx or albnginx
ingress.ui.hostUI hostname"" (required)
ingress.api.hostDedicated API hostname (optional)"" (falls back to ui host)
ingress.realtimePlane.hostDedicated realtime hostname (optional)""
ingress.tlsTLS configuration[]
ingress.websocket.enabledAdd WebSocket-friendly annotations for the realtime planefalse
ingress.annotationsAdditional ingress annotations{}

Services (appApi and appFrontend)

Both services accept the same shape.

KeyDescriptionDefault (appApi)Default (appFrontend)
<svc>.enabledEnable the servicetruetrue
<svc>.replicaCountReplicas22
<svc>.image.repositoryImage repositorycpk-intelligence-app-apicpk-intelligence-app-frontend
<svc>.image.tagImage tag (defaults to chart appVersion)""""
<svc>.resourcesCPU/memory requests and limits250m / 512Mi100m / 128Mi
<svc>.autoscaling.enabledEnable HPAtruefalse
<svc>.autoscaling.minReplicasHPA minimum22
<svc>.autoscaling.maxReplicasHPA maximum104
<svc>.serviceAccount.annotationsAnnotations on the ServiceAccount (IRSA, workload identity){}{}

Realtime gateway

KeyDescriptionDefault
realtimeGateway.enabledEnable the gatewayfalse
realtimeGateway.hostPHX_HOST override""
realtimeGateway.existingSecretSecret containing RUNNER_AUTH_SECRET and SECRET_KEY_BASE""
realtimeGateway.beam.clustering.enabledBEAM clustering across replicastrue
realtimeGateway.beam.cookieSecret.nameSecret containing the BEAM cookiecpki-beam-cookie

Enabling the realtime gateway requires that either realtimeGateway.existingSecret is set, or that externalSecrets.secrets.realtimeGateway.enabled or selfHostedSecrets.enabled is true — the chart fails validation otherwise.

External Secrets Operator integration

KeyDescriptionDefault
externalSecrets.enabledGenerate ExternalSecret resourcestrue
externalSecrets.store.kindClusterSecretStore or SecretStoreClusterSecretStore
externalSecrets.store.nameSecretStore name"" (required when enabled)
externalSecrets.refreshIntervalHow often ESO syncs1h
externalSecrets.secrets.*Per-secret mappings — see values.yaml

Bundled subcharts (evaluation only)

KeyDescriptionDefault
postgresql.enabledDeploy in-cluster Postgresfalse
postgresql.auth.passwordPostgres password (set at deploy time)""
redis-subchart.enabledDeploy in-cluster Redis (aliased to avoid collision with redis.*)false
redis-subchart.auth.passwordRedis password""
keycloak.enabledDeploy bundled Keycloak for quick evalfalse

Object storage (realtime gateway event persistence)

KeyDescriptionDefault
objectStorage.enabledPersist AG-UI events from the realtime gateway to S3-compatible storagefalse
objectStorage.bucketBucket name""
objectStorage.regionBucket regionus-east-1
objectStorage.endpointS3-compatible endpoint override""
objectStorage.existingSecretSecret with static access keys (optional if using IRSA)""

Database migrations

KeyDescriptionDefault
migrations.enabledRun the migrations Job as a pre-install/pre-upgrade hookfalse
migrations.image.repositoryMigrations image repositorycpk-intelligence-db-migrations
migrations.activeDeadlineSecondsJob deadline1800
migrations.backoffLimitRetry count before failing3

Pod-level controls

Per-service keys podDisruptionBudget, podAntiAffinity, and networkPolicy are available for high-availability and traffic-isolation requirements. See values.yaml for full shapes.

Next steps

  • Understand how it works: How the Intelligence Platform Works — architecture, multi-tenancy model, platform layering, and the decision between hosted and self-hosted
  • Premium features overview: CopilotKit Premium — all premium capabilities that require an Intelligence license
  • Use threads in your app: Threads — the persistent-conversation surface powered by the Intelligence Platform you just deployed