LightRAG/specs/001-multi-workspace-server/quickstart.md
Clément THOMAS 62b2a71dda feat(api): add multi-workspace server support for multi-tenant deployments
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>
2025-12-01 12:07:22 +01:00

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-WORKSPACE header → use specified workspace
  • Requests without header → use default workspace

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-WORKSPACE header → 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-WORKSPACE header 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).