Skip to main content
By default Context7 On-Premise runs as a single container. It stores everything on a local volume: a SQLite database for configuration and metadata, and a LanceDB index for embeddings. This is simple to operate but limits you to one instance, since SQLite does not support concurrent writers across containers. To run multiple replicas behind a load balancer, move that state to PostgreSQL. Relational data goes to Postgres, and vectors go to Postgres too via the pgvector extension. Once state is external, every replica is stateless and interchangeable, and you can scale horizontally. Postgres is the only external dependency, there is no separate vector database or object storage to run. If you already run Milvus or Zilliz Cloud, you can send vectors there instead of pgvector. See Vector stores for all the options; this page uses the default pgvector setup throughout.
You only need this if a single container can no longer handle your query or indexing load. Most deployments run fine on one container with a persistent volume. See Docker and Kubernetes for the standard single-container setup.

Architecture

Every replica runs the full application (REST API, MCP endpoint, and parser) and is interchangeable. All shared state lives in PostgreSQL, so a load balancer can spread traffic across any number of replicas. Replicas coordinate through Postgres: they pull indexing jobs from a shared queue, run jobs that must happen once (backups, usage reporting, GitOps sync) on a single replica using a Postgres lock, and validate each other’s signed session tokens. See How it behaves for the details.

What you need

  • A PostgreSQL database (managed or self-hosted) with the pgvector extension, reachable from every replica. Managed Postgres such as RDS, Cloud SQL, and Azure support pgvector; for self-hosted use the pgvector/pgvector image. To keep vectors in Milvus or Zilliz Cloud instead, see Vector stores.
  • A load balancer in front of the replicas.
The embedded SQLite and LanceDB are not used once DATABASE_URL is set.

Configuration

Set the same values on every replica. Presence of DATABASE_URL is what switches the store from SQLite to Postgres and vectors to pgvector.
ENCRYPTION_KEY must be identical on every replica and must stay stable. If it changes, stored credentials can no longer be decrypted and all sessions are invalidated. Store it in your secret manager.

Enable it

1

Provision PostgreSQL with pgvector

Context7 stores vectors with pgvector. You need PostgreSQL 13 or newer with pgvector 0.5.0 or newer (0.5.0 added the HNSW index used for similarity search).On first boot Context7 runs CREATE EXTENSION IF NOT EXISTS vector and creates its tables and indexes automatically. You just provide a database whose user is allowed to create the extension. On managed Postgres that usually means enabling pgvector for the instance first, then connecting with a privileged role.
pgvector ships with RDS and Aurora PostgreSQL. Connect as the master user (or a member of rds_superuser) and enable it:
No parameter group changes are required. See pgvector on Amazon RDS.
Verify the installed version (must be 0.5.0 or newer):
If the application’s database user cannot run CREATE EXTENSION (common on locked-down managed instances), have a DBA run it once as a privileged role before you start Context7. Once the extension exists, the app user only needs normal table privileges.
2

Generate the shared secrets

3

Set the environment on every replica

Add DATABASE_URL and ENCRYPTION_KEY to each replica, alongside the usual LICENSE_KEY and model settings.
4

Point health checks at /api/ready

Configure your load balancer or orchestrator readiness check to use GET /api/ready. It returns 200 only when the replica can reach the database and its MCP bridge is up, so traffic is routed only to replicas that are ready.
5

Scale out

Start as many replicas as you need behind the load balancer. Every replica is identical: it serves API, MCP, and search traffic and pulls parse jobs from the shared queue, so adding replicas grows both query capacity and indexing throughput at once. Work is distributed automatically and no replica is special.If a large re-index competes with query latency on a replica, lower Max concurrent parses (Settings > Indexing) so each replica runs fewer parses at a time, or give replicas more CPU and memory.

Docker Compose

For evaluation and small deployments, this stack brings up Postgres (with pgvector), three app replicas, and an nginx load balancer with one command. There is no object storage to run. Create a .env:
Create nginx.conf (round-robins across the replicas):
Create docker-compose.yml:
Then start it and open http://localhost:3000 to complete the setup wizard:
For production, point DATABASE_URL at an external managed Postgres (with pgvector) and drop the postgres service.

Kubernetes

This assumes you created the namespace and image pull secret in the Kubernetes guide. Put the shared configuration in a Secret:
secret.yaml
Run the app as a Deployment instead of the single-replica StatefulSet. State is external, so /data is per-pod scratch and readiness gates on /api/ready:
deployment.yaml
Reuse the Service and Ingress from the Kubernetes guide, then apply:
A Helm chart renders all of this from a few values (single replica by default, scaling.enabled=true to scale out) and ships with the enterprise distribution.

How it behaves

  • Shared work queue. All replicas pull parse jobs from the same queue in PostgreSQL, so indexing throughput scales with replica count.
  • Single execution for scheduled jobs. Jobs that must run once across the fleet, such as the usage report, scheduled backups, and GitOps sync, take a short Postgres advisory lock when they fire, so exactly one replica runs each. No replica is special, and there is nothing to fail over.
  • Stateless sessions. Sessions are signed tokens, so any replica accepts a login issued by another, and logins survive restarts.
  • Vectors in Postgres. Embeddings are stored with pgvector, indexed with HNSW using cosine distance. The vector dimension is taken from your embedding model on first write, so there is no manual schema setup and switching embedding models is a re-index, not a migration. To store vectors in Milvus or Zilliz Cloud instead, see Vector stores.

Migrating an existing deployment

If you already run a single container, a built-in command copies your existing SQLite data and LanceDB vectors into PostgreSQL, so you do not have to re-index your libraries or re-embed anything. It reads the data directory from your single container and writes to the target PostgreSQL. The admin UI shows your current storage mode and the exact migration command under Settings > Scaling:
Migration helper under Settings, Scaling
Run the migration during a maintenance window. Stop indexing and let in-flight parses finish first, then run the migration against a quiet database. In-flight parse jobs are not migrated.
1

Provision PostgreSQL with pgvector

Create the target PostgreSQL database with pgvector, the same way as in Enable it above. The schema, extension, and vector tables are created automatically.
2

Run the migration

Run the one-shot migrate command with access to your existing data directory and the target database. It creates the schema and copies your projects, pages, users, API keys, per-library access rules, SSO group memberships, settings, usage counters, and the vector index into pgvector.
Use the same volume (context7-data) your single container uses, so the command reads your existing database and vectors.
The command prints the rows and vectors copied, and the ENCRYPTION_KEY your new deployment must use.
3

Reuse the encryption key

Your stored credentials are encrypted with the key from the single container. Set ENCRYPTION_KEY on the new deployment to the value the migration command printed, so credentials stay readable.
4

Redeploy in multi-replica mode

Redeploy with DATABASE_URL and ENCRYPTION_KEY set on every replica, following the steps above. Verify at /api/health that your libraries are present, then scale up.
After redeploying, Settings > Scaling confirms the deployment is running in multi-replica mode:
Multi-replica mode confirmed

Limitations

  • With pgvector, vector search runs in Postgres. For typical on-prem workloads this is well within reach; for a very large index, size Postgres accordingly.
  • Database backups are handled by PostgreSQL in this mode. Use your provider’s managed backups or pg_dump rather than the built-in backup, which targets the embedded SQLite file. See Backup and Restore.