create-ragen-app instead; come back here when you want to understand and control what it did.
Prerequisites
Ensure the following are in place before you begin:- Docker and Docker Compose — all backing services run in containers.
- Node.js 24.x — required if you are running the applications outside of containers.
- ~8 GB of RAM for the full stack. Document parsing is the most memory-intensive component; see the services table below for a breakdown.
Manual deployment
1
Start the backing services
From the root of your cloned repository, bring up the full set of infrastructure services:This starts Postgres, Qdrant, Temporal, Docling, and Redis in Docker. The applications themselves run on the host with hot reload.
If you only want to query existing knowledge bases and do not need document ingestion, use the lighter stack instead — it omits Temporal, so there is nothing for the worker to connect to:To run every application in containers as well,
npm run ragen:up:everything builds and starts the web app, API, worker, and admin panel alongside all services.2
Install dependencies
Install Node.js dependencies across the entire monorepo:
3
Generate the Prisma client
The Prisma client is generated from the schema and is not committed to the repository. Nothing builds without it:
4
Run database migrations
Apply the Prisma schema to your database:This command reads
DATABASE_URL directly from the environment — make sure that variable is set before you run it.5
Seed the database
Populate the database with the default configuration that every new organization requires:This step is not optional — without it, organization creation fails. It reads configuration from
.env.local.6
Start the applications
Start each application in a separate terminal. There is no single root
dev command because these are separate processes with separate lifetimes:apps/worker is what makes an uploaded document searchable. It is not in docker-compose.yml — it runs on the host and connects to Temporal. Without it, uploads succeed and then sit unparsed indefinitely. The worker validates its own environment at boot and exits with the missing variable named rather than starting in a broken state.Services
Every service has a specific role in the stack. Removing one affects only the capabilities it provides.Redis deserves a careful reading of “optional.”
apps/worker requires REDIS_URL and refuses to start without it — it caches organization settings there. apps/web treats its absence as a real mode rather than a degraded one: settings are computed directly and the public chatbot rate limiter fails open, so rate limiting is disabled rather than enforced. apps/api never reads Redis at all; its rate limiter is in-memory.Feature flags
Feature flags are environment variables that turn capabilities on or off. Set them in your.env.local file.
The following flags are off by default — set them to 1 to enable:
The following flag is on by default — set it to
0 to disable:
PII masking is configuration, not a flag
FEATURE_FLAG_PII_MASKING belongs in neither table above. Masking is on when
both PRESIDIO_ANALYZER_URL and PRESIDIO_ANONYMIZER_URL are set, and the
flag exists only to switch it off again:
1 enables nothing on its own. The two Presidio containers use
roughly 1 GB of RAM together and sit behind --profile pii, which is why this
is opt-in at all. See PII masking.
Multi-query expansion has no environment flag. It is a per-organization setting — defaulting to on — under Organization → RAG settings, alongside per-organization toggles for reranking and content moderation.
Minimum environment
The variables below are the minimum required for a working Ragen installation. Copy this block into your.env.local and replace each placeholder with a real value.
.env.local
The model lines above are a sketch, and the route table is the part they leave out. Ragen calls providers itself through a route table that maps each model id to an upstream, and the table shipped in the repository names Azure, Bedrock, Vertex and Scaleway — providers a new installation has no credentials for. A key alone therefore is not enough: the routes have to name the provider that key belongs to.The least error-prone way to get all of it consistent — key, model ids, embeddings model and its vector size, and a matching route table — is to scaffold the install with
npx create-ragen-app, which asks which provider you have and writes the table for it. To wire it by hand instead, write your own file and point LLM_ROUTES_PATH at it.ENCRYPTION_PROVIDER, a persistent storage path or S3 credentials, and DOCLING_STRICT=1 if documents must not leave your network. The full variable reference — including which provider makes which variable mandatory — is in the configuration reference.
Running without internet access
Ragen’s architecture is designed to support fully air-gapped deployments: the model layer is decoupled behind a route table or a proxy of your choosing, and document parsing is local by default. Two honest caveats apply before you commit to this path. It is deployment work, not a flag. Serving a capable model on your own hardware means provisioning GPU capacity. Locally-served open models generally perform less well than commercial ones on complex questions — how much less depends on your documents and your questions, so measure it on your own material first. Container images are pulled at install time. After that, outbound traffic can be cut entirely, with updates delivered as images to your internal registry.Running in production
A local development setup is not suitable for production. Three areas require deliberate decisions before you expose Ragen to real users.Encryption
Ragen starts normally with no key provider and stores message content unencrypted. That is fine for local development and wrong for production. SetENCRYPTION_PROVIDER to one of the following values and supply the matching key material:
Verify encryption is active by creating a new thread after the change — if the API returns the thread without error and subsequent messages are readable, encryption is working correctly.
Storage
STORAGE_PROVIDER=local writes uploaded files to STORAGE_LOCAL_PATH (defaults to ./data/storage). Mount a persistent volume at that path, or a restart loses every uploaded document.
For production and multi-replica setups, use STORAGE_PROVIDER=s3, which works with any S3-compatible object store:
.env.local
Use
S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY — not AWS_-prefixed variables. The AWS_ prefix is reserved for real AWS Bedrock and KMS configuration, which a deployment can use at the same time as non-AWS S3 storage.Embedding model and vector dimensions
EMBEDDINGS_MODEL defaults to bge-multilingual-gemma2, which produces 3584-dimensional vectors. VECTOR_SIZE must match this value, or Qdrant rejects every upsert.
If you switch to a different embedding model, update VECTOR_SIZE accordingly: