Enable a single LightRAG server instance to serve multiple isolated workspaces via HTTP header-based routing. This allows multi-tenant SaaS deployments where each tenant's data is completely isolated. Key features: - Header-based workspace routing (LIGHTRAG-WORKSPACE, X-Workspace-ID fallback) - Process-local pool of LightRAG instances with LRU eviction - FastAPI dependency (get_rag) for workspace resolution per request - Full backward compatibility - existing deployments work unchanged - Strict multi-tenant mode option (LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=false) - Configurable pool size (LIGHTRAG_MAX_WORKSPACES_IN_POOL) - Graceful shutdown with workspace finalization Configuration: - LIGHTRAG_DEFAULT_WORKSPACE: Default workspace (falls back to WORKSPACE) - LIGHTRAG_ALLOW_DEFAULT_WORKSPACE: Require explicit header when false - LIGHTRAG_MAX_WORKSPACES_IN_POOL: Max concurrent workspace instances (default: 50) Files: - New: lightrag/api/workspace_manager.py (core multi-workspace module) - New: tests/test_multi_workspace_server.py (17 unit tests) - New: render.yaml (Render deployment blueprint) - Modified: All route files to use get_rag dependency - Updated: README.md, env.example with documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.9 KiB
Quickstart: Multi-Workspace LightRAG Server
Date: 2025-12-01 Feature: 001-multi-workspace-server
Overview
This guide shows how to deploy LightRAG Server with multi-workspace support, enabling a single server instance to serve multiple isolated tenants.
Configuration
Environment Variables
Add these new environment variables to your deployment:
# Multi-workspace configuration
LIGHTRAG_DEFAULT_WORKSPACE=default # Workspace for requests without header
LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=true # Allow requests without workspace header
LIGHTRAG_MAX_WORKSPACES_IN_POOL=50 # Max concurrent workspace instances
# Existing configuration (unchanged)
WORKSPACE=default # Backward compatible, used if DEFAULT_WORKSPACE not set
WORKING_DIR=/data/rag_storage # Base directory for all workspace data
INPUT_DIR=/data/inputs # Base directory for workspace input files
Configuration Modes
Mode 1: Backward Compatible (Default)
No changes needed. Existing deployments work unchanged.
# .env file
WORKSPACE=my_workspace
All requests use my_workspace regardless of headers.
Mode 2: Multi-Workspace with Default
Allow multiple workspaces, with a fallback for headerless requests.
# .env file
LIGHTRAG_DEFAULT_WORKSPACE=default
LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=true
LIGHTRAG_MAX_WORKSPACES_IN_POOL=50
- Requests with
LIGHTRAG-WORKSPACEheader → use specified workspace - Requests without header → use
defaultworkspace
Mode 3: Strict Multi-Tenant
Require workspace header on all requests. Prevents accidental data leakage.
# .env file
LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=false
LIGHTRAG_MAX_WORKSPACES_IN_POOL=100
- Requests with
LIGHTRAG-WORKSPACEheader → use specified workspace - Requests without header → return
400 Bad Request
Usage Examples
Starting the Server
# Standard startup (works the same as before)
lightrag-server --host 0.0.0.0 --port 9621
# Or with environment variables
export LIGHTRAG_DEFAULT_WORKSPACE=default
export LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=true
lightrag-server
Making Requests
Single-Workspace (No Header)
# Uses default workspace
curl -X POST http://localhost:9621/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"query": "What is LightRAG?"}'
Multi-Workspace (With Header)
# Ingest document to tenant-a
curl -X POST http://localhost:9621/documents/text \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-H "LIGHTRAG-WORKSPACE: tenant-a" \
-d '{"text": "Tenant A confidential document about AI."}'
# Query from tenant-a (finds the document)
curl -X POST http://localhost:9621/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-H "LIGHTRAG-WORKSPACE: tenant-a" \
-d '{"query": "What is this workspace about?"}'
# Query from tenant-b (does NOT find tenant-a's document)
curl -X POST http://localhost:9621/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-H "LIGHTRAG-WORKSPACE: tenant-b" \
-d '{"query": "What is this workspace about?"}'
Python Client Example
import httpx
class LightRAGClient:
def __init__(self, base_url: str, api_key: str, workspace: str | None = None):
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
if workspace:
self.headers["LIGHTRAG-WORKSPACE"] = workspace
async def query(self, query: str) -> dict:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.base_url}/query",
headers=self.headers,
json={"query": query}
)
response.raise_for_status()
return response.json()
# Usage
tenant_a_client = LightRAGClient(
"http://localhost:9621",
api_key="your-api-key",
workspace="tenant-a"
)
tenant_b_client = LightRAGClient(
"http://localhost:9621",
api_key="your-api-key",
workspace="tenant-b"
)
# Each client accesses only its own workspace
result_a = await tenant_a_client.query("What documents do I have?")
result_b = await tenant_b_client.query("What documents do I have?")
Data Isolation
Each workspace has completely isolated:
- Documents: Files ingested in one workspace are invisible to others
- Embeddings: Vector indices are workspace-scoped
- Knowledge Graph: Entities and relationships are workspace-specific
- Query Results: Queries only return data from the specified workspace
Directory Structure
/data/rag_storage/
├── tenant-a/ # Workspace: tenant-a
│ ├── kv_store_*.json
│ ├── vdb_*.json
│ └── graph_*.json
├── tenant-b/ # Workspace: tenant-b
│ ├── kv_store_*.json
│ ├── vdb_*.json
│ └── graph_*.json
└── default/ # Default workspace
└── ...
/data/inputs/
├── tenant-a/ # Input files for tenant-a
├── tenant-b/ # Input files for tenant-b
└── default/ # Input files for default workspace
Memory Management
The workspace pool uses LRU (Least Recently Used) eviction:
- First request to a workspace initializes its LightRAG instance
- Instances stay loaded for fast subsequent requests
- When pool reaches
LIGHTRAG_MAX_WORKSPACES_IN_POOL, least recently used workspace is evicted - Evicted workspaces are re-initialized on next request (data persists in storage)
Tuning Pool Size
| Deployment Size | Recommended Pool Size | Notes |
|---|---|---|
| Development | 5-10 | Minimal memory usage |
| Small SaaS | 20-50 | Handles typical multi-tenant load |
| Large SaaS | 100+ | Depends on available memory |
Memory Estimate: Each workspace instance uses approximately 50-200MB depending on LLM/embedding bindings and cache settings.
Troubleshooting
"Missing LIGHTRAG-WORKSPACE header"
Cause: LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=false and no header provided
Solution: Either:
- Add
LIGHTRAG-WORKSPACEheader to all requests - Set
LIGHTRAG_ALLOW_DEFAULT_WORKSPACE=true
"Invalid workspace identifier"
Cause: Workspace ID contains invalid characters
Solution: Use only alphanumeric characters, hyphens, and underscores. Must start with alphanumeric, max 64 characters.
"Failed to initialize workspace"
Cause: Storage backend unavailable or misconfigured
Solution: Check storage backend connectivity (Postgres, Neo4j, etc.) and verify configuration.
Slow First Request to New Workspace
Expected Behavior: First request to a workspace initializes storage connections.
Mitigation: Pre-warm frequently used workspaces at startup (implementation-specific).