Skip to main content
Deploy Context7 On-Premise on Kubernetes using raw manifests. This guide assumes you have completed the On-Premise setup and have a valid license key.

Prerequisites

  • Kubernetes cluster (v1.24+)
  • kubectl configured for your cluster
  • A StorageClass that supports ReadWriteOnce volumes
  • Context7 license key

Registry Authentication

Context7 Enterprise images are hosted on ghcr.io and require authentication. Create an image pull secret using your license key:

Manifests

Context7 Enterprise runs as a single-replica StatefulSet with persistent storage. The manifests below define the core resources: a StatefulSet for the application, a Service for internal routing, and an Ingress for external access.

StatefulSet

Context7 uses SQLite and LanceDB for local storage, which require a persistent volume. This means it must run as a StatefulSet with a single replica since SQLite does not support concurrent writers. To run multiple replicas, move state to PostgreSQL (with the pgvector extension for embeddings) as described in Scaling.
statefulset.yaml
Storage class: If your cluster does not have a default StorageClass, the PVC will stay in Pending and the pod won’t start. Uncomment storageClassName and set it to a StorageClass available in your cluster (e.g. gp3 on AWS EKS, standard on GKE, default on AKS). Run kubectl get sc to see available options.
Resource sizing: The defaults above (1 CPU / 2 GiB request) work for light usage. If you are parsing many large repositories concurrently, increase the limits. Parsing is CPU and memory intensive due to LLM calls and vector indexing.
Do not set replicas higher than 1 in this configuration. Context7 uses SQLite which only supports a single writer, so running multiple replicas causes database lock errors. To scale out, see Scaling.

Service

service.yaml

Ingress

ingress.yaml
Replace context7.internal.yourcompany.com with your actual hostname and context7-tls with your TLS secret.

Apply Everything

After creating the namespace and secrets in the Registry Authentication step, apply the manifests:
Verify the pod is running:
Once the pod is ready, open your Ingress hostname in a browser to complete the setup wizard.

Running as non-root

The container runs as root by default, so existing deployments are unaffected. Set a securityContext to run it unprivileged, as the manifest above does. This satisfies a runAsNonRoot admission policy. runAsUser can be any UID. Everything Context7 writes lives under /data: fsGroup makes that volume writable by whichever UID the pod runs as, so keep it set if you change runAsUser. The only other path the container writes is /tmp, which is world-writable: it holds Git SSH key material, backup and restore staging, and support bundles.
Changing runAsUser on an existing volume. Files already on the volume belong to the previous UID. Most CSI drivers re-apply ownership from fsGroup when the volume mounts, so the change is transparent. Volumes that do not support ownership management — hostPath, and some NFS setups — ignore fsGroup, and the new UID cannot write the existing files. Chown the volume once, or keep the original UID. The server detects this at startup and names the files it cannot write.
This requires image version 1.3.2 or later. Earlier images keep source clones on a root-owned layer inside the image, so a non-root UID starts normally but fails on the first parse.
If the volume is not writable by the UID you choose, the server exits at startup naming the offending paths rather than failing later:
Upgrading an existing deployment needs no manual chown. The kubelet re-chowns the volume on mount when fsGroup is set. To also set readOnlyRootFilesystem: true, mount an emptyDir at /tmp. Nothing else outside /data needs to be writable.

Networking Requirements

Context7 requires outbound connectivity to the following: If you use NetworkPolicies, ensure egress to these endpoints is allowed:
networkpolicy.yaml
For stricter policies, allow egress on port 443 to the specific domains listed above, and ensure egress to kube-dns on port 53 (UDP/TCP) is permitted for DNS resolution.

Operations

Updating

Pull the latest image and restart:
To pin a specific version:
If your registry token has expired, refresh it before restarting:

Health Monitoring

The /api/health endpoint returns structured JSON with license status, connectivity, and parsed repo count. Point your monitoring stack at it:
Example response:

Logs

Troubleshooting

Pod is in CrashLoopBackOff

Context7 validates your license key on startup. If the key is missing, invalid, or expired, the server exits immediately before the health endpoint is available. This means Kubernetes will report CrashLoopBackOff rather than a failed probe. Check the logs first:
Look for [license] messages in the first few lines. Common causes:
  • Missing or incorrect LICENSE_KEY in the context7-config secret
  • No outbound connectivity to context7.com for license validation
  • Expired license: contact context7@upstash.com to renew
The startup probe only comes into play after the license is validated. If the pod is crash-looping, the issue is always upstream of the probe. Check logs, not probe events.

Scaling

The manifests above run a single replica: a StatefulSet with a ReadWriteOnce volume holding the embedded SQLite database and LanceDB index. This suits most deployments. To run multiple replicas behind the Service, move that state to an external PostgreSQL with the pgvector extension. Relational data and embeddings both live in Postgres, so every pod becomes stateless and interchangeable: swap the StatefulSet for a Deployment with replicas greater than one, mount /data as scratch (emptyDir), and gate readiness on /api/ready so a pod only receives traffic once it has connected to Postgres.
A Helm chart renders all of this from a few values: the single-replica StatefulSet by default, or a multi-replica Deployment with scaling.enabled=true. It ships with the enterprise distribution.
See Scaling for the full setup, PostgreSQL and pgvector provisioning, and migrating an existing single-container deployment.

Connecting AI Clients

Once deployed, point your MCP clients to your Ingress URL. See Connecting Your AI Client for client-specific instructions. Replace localhost:3000 with your Kubernetes Ingress hostname.