Skip to main content

Configuration

The InfoConnect Hybrid Search Engine uses environment variables to control how it works. You can create a .env file in the project root. The system reads this file when it starts.

Getting Started with Configuration

Most users only need to change 3 to 5 settings to get started. The rest can use their default values.

Here is the minimum you need for your first setup:

# API key to client mapping (required for security)
API_KEY_CLIENTS={"my-secret-key-123":"my-client"}

# How fast users can call the API (optional)
RATE_LIMIT=30

# How much detail to show in logs (optional)
LOG_LEVEL=INFO

# Where your services run (default usually works)
REDIS_URL=redis://localhost:6379/0
QDRANT_URL=http://localhost:6333
Quick Start

Copy the example above into a file named .env in your project folder. Replace my-secret-key-123 with a strong random key. Then start the services with just dev.

The sections below explain each setting in detail. You can skip to the Example .env File for a complete reference.

Settings Most People Change

These are the settings you will likely want to adjust first.

API Keys and Access Control

VariableDefaultDescription
API_KEY_CLIENTS{}JSON object mapping valid API keys to client ids for authentication
RATE_LIMIT30Requests per minute per resolved client id for protected routes. See Rate Limiting guide for details.
AUTH_FAILURE_RATE_LIMIT10Failed authentication attempts per minute per source address before a request can resolve to a client
DEBUGfalseEnable debug mode (exposes /docs and /redoc)
CORS_ORIGINS["http://localhost:3000"]Allowed CORS origins

The API_KEY_CLIENTS variable is required. Every request to the API must include one configured key in the X-API-Key header. This setting is parsed as a JSON object where each key maps to the client id that owns the request data. An empty object rejects every API key.

Set DEBUG=true only in development or trusted environments. It exposes /docs for interactive Swagger UI requests and /redoc for a cleaner read-only API reference. Keep DEBUG=false in production unless you intentionally want those documentation endpoints available.

Why Multiple Keys?

You might give different keys to different teams or applications. This lets you rotate keys later without breaking everything at once.

Client isolation

Each API key maps to a client_id that owns the data it ingests. Every request — embed, search, update, delete, list — is automatically scoped to that client. Documents from different clients never mix, even when two clients use the same document_id. You do not pass client_id in your requests; the server resolves it from your API key.

If API_KEY_CLIENTS is empty or missing, the server rejects every request with 401 and logs a loud startup warning. This is intentional: the system fails closed rather than serving unscoped data.

For details on how client scoping affects search filters — including why client_id cannot be used as a filter key — see the Search Filters guide.

Example API Key Clients

# Development - simple keys are fine
API_KEY_CLIENTS={"test-key-123":"demo-client","another-key":"demo-client","super-secret-key":"demo-client"}

# Production - use strong random keys and stable client ids
API_KEY_CLIENTS={"prod-key-REPLACE_WITH_RANDOM_HEX":"prod-client"}

Logging Level

VariableDefaultDescription
LOG_LEVELINFOLogging level (DEBUG, INFO, WARNING, ERROR)

Set this to DEBUG when troubleshooting. Use INFO for normal operation. Use WARNING or ERROR to reduce log noise in production.

Search and Document Settings

These settings control how documents are split and how search results are returned.

Document Chunking

VariableDefaultDescription
CHUNK_SIZE512Approximate tokens per chunk
CHUNK_OVERLAP50Token overlap between chunks

When you upload a document, the InfoConnect Hybrid Search Engine splits it into smaller pieces called chunks. This helps the search work better. Larger chunks mean more context but slower processing. Smaller chunks mean more precise matches but less context.

Choosing Chunk Size

Think of chunks like pages in a book. If your documents are short emails, use small chunks (256). If they are long reports, use larger chunks (1024). The default 512 works well for most documents.

Chunk Size Guidelines

Use CaseChunk SizeOverlap
Short documents (emails)25625
Standard documents51250
Long documents (reports)1024100

Search Results

VariableDefaultDescription
RETRIEVAL_TOP_K10Default search results count
HYBRID_ALPHA0.5Default dense weight for hybrid search; supported range is 0.0 to 1.0
SEARCH_DENSE_SCORE_FLOOR0.70Minimum raw cosine score for a dense candidate
SEARCH_SPARSE_SCORE_FLOOR1.0Minimum raw BM25 dot-product score for a sparse candidate
SEARCH_TOTAL_COUNT_CAP1000Maximum score-gated count scan before reporting a lower bound; /search totals are independently bounded to the 200-result retrieval window
RERANK_TOP_N5Number of results to rerank
RERANK_TIMEOUT_SEC2.0Reranking timeout
MAX_NODES_PER_TASK50Max nodes per search task

RETRIEVAL_TOP_K controls how many results the search returns by default. HYBRID_ALPHA sets the deployment-wide dense/sparse balance; an explicit /search alpha value overrides it for that request. Start with HYBRID_ALPHA=0.5. Lower values favor sparse/lexical matches; higher values favor dense/semantic matches.

The score floors gate raw dense and sparse candidates before hybrid fusion. The recommended defaults are SEARCH_DENSE_SCORE_FLOOR=0.70 and SEARCH_SPARSE_SCORE_FLOOR=1.0; a candidate is retained when either active stream clears its floor. Raising a floor can remove low-confidence noise, but setting it too high over-filters genuine matches and can produce no results. Keep gibberish and other queries with no confident match in the genuine no-results state. Recalibrate the floors when the embedding model or corpus distribution changes. See the Search API reference for the full mechanism.

OCR and Classification Settings

These settings control how the InfoConnect Hybrid Search Engine reads images and categorizes documents.

Document Classification

VariableDefaultDescription
CLASSIFICATION_ENABLEDtrueEnable automatic document classification
CLASSIFIER_BACKENDmodernbert-onnxClassification backend selector

When you upload a document, the InfoConnect Hybrid Search Engine can automatically figure out what category it belongs to. This helps organize your documents.

Model Roles

RAPIDOCR_MODEL_NAME is used for OCR during parsing. ModernBERT ONNX is the only supported classification backend.

These model values reflect the repository's current .env.example / Docker mapping.

OCR for Images and PDFs

VariableDefaultDescription
RAPIDOCR_OCR_ENABLEDtrueMaster switch to enable/disable OCR
RAPIDOCR_MODEL_NAMErapidocr-onnxruntimeOCR model for images and low-text PDF pages
OCR_PAGE_TEXT_THRESHOLD80Min characters before OCR fallback

If you upload an image or a scanned PDF, the InfoConnect Hybrid Search Engine uses RapidOCR to read the text. The threshold setting controls when it decides a PDF page needs OCR help.

Custom Categories

VariableDefaultDescription
ALLOWED_CATEGORIES["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"]JSON array of category labels scored by ModernBERT
CATEGORY_DESCRIPTIONSKYC/Credit/Collateral/Ledger descriptionsOptional JSON object mapping category labels to richer scoring text. Missing descriptions fall back to the category label.
Default Categories

Current defaults come from app/config.py and use the ModernBERT financial / corporate taxonomy:

["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"]

These settings are also parsed as JSON arrays. If you customize them, keep the JSON syntax:

ALLOWED_CATEGORIES=["financial reporting","technology","other"]

You can customize these to match your business, but the classifier still expects JSON arrays rather than plain delimited strings.

CATEGORY_DESCRIPTIONS is optional. You do not need to provide a description for every item in ALLOWED_CATEGORIES. If a category has no matching description, the InfoConnect Hybrid Search Engine falls back to the label text itself. For example, Ledger without a description is scored as Ledger.

Descriptions are strongly recommended for short, ambiguous, acronym-based, or client-specific labels. Labels such as KYC, Credit, Collateral, and Ledger often need richer text because real documents may say Kad Pengenalan, bankruptcy search, pledged asset, or journal entry instead of the exact label.

ALLOWED_CATEGORIES=["KYC","Credit","Collateral","Ledger"]
CATEGORY_DESCRIPTIONS={"KYC":"know your customer, identity verification, customer due diligence, IC, passport, Kad Pengenalan, Warganegara, onboarding, or beneficial ownership documents","Credit":"credit reports, bankruptcy searches, insolvency checks, lending, repayment history, or credit risk documents"}

You can describe only the categories that need extra context. The API still returns the configured category label, such as KYC; descriptions are used only for scoring and strong lexical evidence matching.

ModernBERT ONNX Settings

VariableDefaultDescription
MODERNBERT_ONNX_MODEL_REPOonnx-community/ModernBERT-base-nli-ONNXHugging Face repo for the ONNX classifier
MODERNBERT_ONNX_MODEL_FILEonnx/model_quantized.onnxONNX model file loaded from the repo
MODERNBERT_ONNX_CATEGORY_TEMPLATEThe topic of this document is {label}.Hypothesis template for category scoring
MODERNBERT_ONNX_CATEGORY_THRESHOLD0.45Minimum score required to keep a category
MODERNBERT_ONNX_LONG_DOC_WINDOW1800Sliding window size (tokens) for documents exceeding model context
MODERNBERT_ONNX_LONG_DOC_OVERLAP256Token overlap between consecutive windows
MODERNBERT_ONNX_TOP_K_CHUNKS2Max additional chunks to score after the first (long-doc optimization)
MODERNBERT_ONNX_NLI_MAX_TOKENS2048Maximum token length for NLI input sequences (premise + hypothesis). Lower values reduce inference time at the cost of truncating long document text
CLASSIFICATION_SELECT_TOP_K3Number of representative chunks selected via centroid + MMR for post-embedding classification. Classification scores these chunks instead of the full document text.
MODERNBERT_ONNX_INTRA_OP_THREADS1ONNX Runtime intra-op thread count
MODERNBERT_ONNX_INTER_OP_THREADS1ONNX Runtime inter-op thread count
MODERNBERT_PREFILTER_ENABLEDfalseEnable lexical prefilter to reduce hypothesis space before ONNX inference
MODERNBERT_PREFILTER_TOP_K3Number of top categories to keep after lexical prefilter

Available Model Repos (MODERNBERT_ONNX_MODEL_REPO)

The classifier requires a 3-class NLI model outputting entailment (index 0), neutral (index 1), contradiction (index 2). Only repos with this exact label mapping are compatible.

RepoSizeNLI ClassesStatus
onnx-community/ModernBERT-base-nli-ONNXBase (768d, 22 layers)3 (entailment / neutral / contradiction)✅ Compatible
onnx-community/ModernBERT-large-zeroshot-v2.0-ONNXLarge (1024d, 28 layers)2 (entailment / not_entailment)❌ Incompatible — crashes at runtime (logits.shape[1] < 3)
warning

Do not use 2-class models like ModernBERT-large-zeroshot-v2.0-ONNX. The pipeline expects exactly 3 NLI output logits and will raise a shape validation error on startup or during inference.

Available Model Files (MODERNBERT_ONNX_MODEL_FILE)

These files exist in the upstream repository, but the current commercial-use allowlist accepts only onnx/model_quantized.onnx. Selecting another file through an environment override fails startup. Adding another artifact requires a code and policy change with license evidence and review.

FilePrecisionSizeNotes
onnx/model.onnxfp32~599 MBHighest accuracy; not currently allowlisted.
onnx/model_fp16.onnxfp16~300 MBNear-lossless accuracy. Good balance for most deployments.
onnx/model_quantized.onnxint8~151 MBApproved default; good accuracy/size tradeoff.
onnx/model_int8.onnxint8~151 MBSame quantization as model_quantized.onnx.
onnx/model_uint8.onnxuint8~151 MBAlternative 8-bit quantization.
onnx/model_q4.onnx4-bit~225 MBAggressive quantization. Lower accuracy, smaller download.
onnx/model_q4f16.onnx4-bit/fp16~140 MBSmallest file. Noticeable accuracy loss.
onnx/model_bnb4.onnxBNB 4-bit~218 MBBitsAndBytes-style 4-bit quantization.
Approved model file

Use onnx/model_quantized.onnx for current deployments. Other upstream artifacts are reference options only until explicitly reviewed and added to the commercial-use allowlist.

Post-Embedding Classification

Classification runs asynchronously after embedding and typically takes 2-4 seconds per document. The classifier reads chunk embeddings from Qdrant, selects representative chunks via centroid + MMR, and scores only those chunks with ModernBERT. See Architecture: Post-Embedding Classification for the trigger, retry, and reconciliation contract.

note

Thread counts default to 1 across all environments. Increase MODERNBERT_ONNX_INTRA_OP_THREADS for CPU-bound preprocessing workers if your deployment has spare cores.

Named Entity Recognition

VariableDefaultDescription
SPACY_NER_ENABLEDtrueEnable spaCy NER extraction
NER_ENTITY_KEYSSee belowEntity types to extract and store

The InfoConnect Hybrid Search Engine can find and extract important information from your documents. This includes names, dates, amounts of money, and more.

Default Entity Keys

The following entity types are extracted and stored:

  • persons — People names
  • organizations — Companies, agencies
  • dates — Date and time references
  • locations — Places, countries, cities
  • monetary_amounts — Currency values (regex-enhanced)
  • account_numbers — Account identifiers (regex)
  • transaction_refs — Transaction references (regex)
  • account_types — Account types (regex)

Worker and Performance Settings

These settings control background processing and resource usage.

Celery Worker Settings

VariableDefaultDescription
CELERY_WORKER_PREFETCH_MULTIPLIER1Tasks prefetched per worker
CELERY_WORKER_REVISIONunknownDeployment marker used by the startup worker-isolation guard
CELERY_TASK_ACKS_LATEtrueAcknowledge after task completion
CELERY_WORKER_MAX_MEMORY_PER_CHILD1572864 (1.5GB)Memory limit per worker (KB)
CELERY_WORKER_MAX_TASKS_PER_CHILD100Tasks before worker restart
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING250Embedding-worker task recycle target from config.py
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING500Preprocessing-worker task recycle target from config.py
CELERY_TASK_MAX_RETRIES3Max retry attempts for failed tasks
CELERY_RETRY_BACKOFF_BASE60Base seconds for exponential backoff
CELERY_RESULT_EXPIRES86400 (24h)Result retention time (seconds)
CELERY_EMBED_SOFT_TIME_LIMIT_SEC900Soft time limit for embedding tasks
CELERY_EMBED_TIME_LIMIT_SEC1200Hard time limit for embedding tasks
CELERY_TASK_REJECT_ON_WORKER_LOSTtrueRequeue task if a worker exits unexpectedly
What Are Workers?

Workers are background processes that handle document processing. When you upload a file, the API accepts it immediately. Then workers do the heavy lifting: parsing, chunking, embedding, and storing. This happens in the background so the API stays fast.

Workers automatically restart after processing a certain number of tasks or using too much memory. This prevents memory leaks from slowing things down.

just dev and just prod-up set CELERY_WORKER_REVISION to the current 12-character Git revision and include that value in every worker node name. If you start the API or workers manually, use the same valid revision marker for all processes and name workers as preprocessing-<revision>@<host> or embedding-<number>-<revision>@<host>.

Code defaults vs Docker Compose defaults

app/config.py keeps the general CELERY_WORKER_MAX_TASKS_PER_CHILD default at 100, but docker-compose.yml overrides the live worker values to 250 for embedding workers and 500 for the preprocessing worker.

app/config.py defaults CELERY_WORKER_MAX_MEMORY_PER_CHILD to 1572864 KB (about 1.5 GB). The preprocessing worker in docker-compose.yml can override this via PROD_PREPROCESSING_MAX_MEMORY_PER_CHILD_KB (default 4194304 KB / 4 GB) to match its larger container memory limit.

Docker / Runtime-only Overrides

VariableDefaultDescription
PROD_PREPROCESSING_MEMORY6GDocker Compose memory limit for worker-preprocessing-1

This variable is only used by docker-compose.yml at runtime. It does not exist in app/config.py, but it controls the container memory limit for the preprocessing worker in production-style Docker runs.

Redis Configuration

VariableDefaultDescription
REDIS_URLredis://localhost:6379/0Redis connection for caching
CELERY_BROKER_URLredis://localhost:6379/1Celery task queue
CELERY_RESULT_BACKENDredis://localhost:6379/2Celery results store

Redis is used for two things: caching and task queuing. The URLs point to different databases on the same Redis server.

Redis Database Usage

DBPurpose
0Application caching
1Celery task broker
2Celery results backend

Qdrant Configuration

VariableDefaultDescription
QDRANT_URLhttp://localhost:6333Qdrant server URL
COLLECTION_NAMEdocumentsVector collection name
VECTOR_SIZE384Embedding dimensions (must match model)

Qdrant is the vector database that stores your document embeddings. This is what makes semantic search possible.

Embedding Models

VariableDefaultDescription
DENSE_MODEL_NAMEBAAI/bge-small-en-v1.5Dense embedding model
SPARSE_MODEL_NAMEQdrant/bm25Sparse/BM25 model
RERANK_MODEL_NAMEXenova/ms-marco-MiniLM-L-6-v2FastEmbed cross-encoder reranker
EMBEDDING_BATCH_SIZE32Embeddings per batch
FASTEMBED_CACHE_DIR./models/fastembedModel cache location

Embedding models turn text into numbers that capture meaning. The InfoConnect Hybrid Search Engine uses two types: dense (for semantic meaning) and sparse (for keyword matching). Together they give better search results.

The listed model identifiers are the reviewed commercial-use defaults. Arbitrary environment overrides are rejected; changing a model requires updating the runtime and compliance allowlists.

Upstream Dense Model References

ModelDimensionsSpeedQualityCommercial allowlist
BAAI/bge-small-en-v1.5384FastGoodApproved default
BAAI/bge-base-en-v1.5768MediumBetterNot reviewed
BAAI/bge-large-en-v1.51024SlowBestNot reviewed
sentence-transformers/all-MiniLM-L6-v2384FastGoodNot reviewed
About Model Size

Larger models may improve quality but use more memory and run slower. They are reference options, not runtime configuration choices, until their licenses and artifacts are reviewed and allowlisted.

File Upload Security

VariableDefaultDescription
MAX_FILE_SIZE_MB200Maximum upload file size in MB
ALLOWED_EXTENSIONS.pdf,.docx,.txt,.md,.text,.pptx,.ppt,.xlsx,.xls,.png,.jpg,.jpeg,.tif,.tiffPermitted file extensions
ALLOWED_MIME_TYPESSee example belowPermitted upload MIME types mapped to extensions
UPLOAD_DIR./models/uploads (/tmp/ic-uploads in Docker examples)Local staging directory for files before Celery processing
OFFICE_LEGACY_CONVERSION_ENABLEDfalseEnable LibreOffice conversion of legacy .ppt/.xls to OOXML before parsing
LIBREOFFICE_BINARYsofficeLibreOffice executable used for legacy Office conversion
OFFICE_LEGACY_CONVERSION_TIMEOUT_SEC120Timeout for a single legacy Office conversion
OFFICE_MAX_UNCOMPRESSED_BYTES1000000000Max decompressed size of an OOXML (.docx/.pptx/.xlsx) upload (zip-bomb guard)
OFFICE_MAX_COMPRESSION_RATIO200Max uncompressed:compressed ratio for an OOXML upload (zip-bomb guard)

These settings protect your server from oversized uploads and unexpected file types. A file must pass the extension check, declared MIME mapping check when a known MIME type is provided, and sniffed-content check before it is enqueued.

UPLOAD_DIR is especially important in Docker deployments. The API writes the uploaded file there first, then the preprocessing worker reads it from the same path. If you change UPLOAD_DIR, make sure the API and worker containers mount the same shared volume at that location.

Production Runtime Threading

VariableDefaultDescription
PROD_EMBEDDING_OMP_THREADS1Auto-tuned OpenMP/BLAS thread count for embedding worker containers
OMP_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeOpenMP thread cap inside embedding workers
OPENBLAS_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeOpenBLAS thread cap inside embedding workers
MKL_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeMKL thread cap inside embedding workers

scripts/generate_prod_runtime.py writes PROD_EMBEDDING_OMP_THREADS to .runtime/prod.env; Docker Compose passes it through to the lower-level numerical libraries. Keep this at 1 unless benchmark evidence shows spare CPU headroom.

Model Cache Paths

VariableDefaultDescription
FASTEMBED_CACHE_DIR./models/fastembedFastEmbed model cache
HF_HOME/app/.cache/huggingfaceHugging Face cache
HF_TOKEN(unset)Optional Hugging Face token for gated or rate-limited model downloads
SPACY_MODEL_NAMEen_core_web_smspaCy NER model

These control where approved models are stored. You usually do not need to change them. Set HF_TOKEN only when downloads of the approved Hugging Face repository are rate-limited; switching repositories or spaCy models requires a reviewed code and compliance-policy change.

Example .env File

Here is a complete example .env file with all common settings, organized by ergonomics — the settings you change most often appear first:

# =============================================================================
# 1. Identity & Access
# =============================================================================
API_KEY_CLIENTS={"dev-key-123":"dev-client","dev-key-456":"dev-client"}
RATE_LIMIT=60
AUTH_FAILURE_RATE_LIMIT=10

# =============================================================================
# 2. Core Behavior
# =============================================================================
DEBUG=true
CORS_ORIGINS=["http://localhost:3000","http://localhost:8080"]
LOG_LEVEL=INFO
RETRIEVAL_TOP_K=10
HYBRID_ALPHA=0.5
SEARCH_DENSE_SCORE_FLOOR=0.70
SEARCH_SPARSE_SCORE_FLOOR=1.0
SEARCH_TOTAL_COUNT_CAP=1000
RERANK_TOP_N=5
MAX_FILE_SIZE_MB=200
ALLOWED_EXTENSIONS=[".pdf",".docx",".txt",".md",".text",".pptx",".ppt",".xlsx",".xls",".png",".jpg",".jpeg",".tif",".tiff"]
ALLOWED_MIME_TYPES={"application/pdf":".pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document":".docx","application/vnd.openxmlformats-officedocument.presentationml.presentation":".pptx","application/vnd.ms-powerpoint":".ppt","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":".xlsx","application/vnd.ms-excel":".xls","text/plain":".txt","text/markdown":".md","image/png":".png","image/jpeg":".jpg"}

# =============================================================================
# 3. Domain Taxonomy (Client-Customizable)
# =============================================================================
ALLOWED_CATEGORIES=["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"]
NER_ENTITY_KEYS=["persons","organizations","dates","locations","monetary_amounts","account_numbers","transaction_refs","account_types"]

# =============================================================================
# 4. Pipeline Tuning
# =============================================================================
CHUNK_SIZE=512
CHUNK_OVERLAP=50
EMBEDDING_BATCH_SIZE=32
MAX_NODES_PER_TASK=50
RAPIDOCR_OCR_ENABLED=true
OCR_PAGE_TEXT_THRESHOLD=80

# =============================================================================
# 5. Classification & Entity Extraction
# =============================================================================
CLASSIFICATION_ENABLED=true
SPACY_NER_ENABLED=true
CLASSIFIER_BACKEND=modernbert-onnx
SPACY_MODEL_NAME=en_core_web_sm
CLASSIFICATION_SELECT_TOP_K=3
MODERNBERT_ONNX_MODEL_REPO=onnx-community/ModernBERT-base-nli-ONNX
MODERNBERT_ONNX_MODEL_FILE=onnx/model_quantized.onnx
MODERNBERT_ONNX_CATEGORY_TEMPLATE="The topic of this document is {label}."
MODERNBERT_ONNX_CATEGORY_THRESHOLD=0.45
MODERNBERT_ONNX_LONG_DOC_WINDOW=1800
MODERNBERT_ONNX_LONG_DOC_OVERLAP=256
MODERNBERT_ONNX_TOP_K_CHUNKS=2
MODERNBERT_ONNX_NLI_MAX_TOKENS=2048
MODERNBERT_ONNX_INTRA_OP_THREADS=1
MODERNBERT_ONNX_INTER_OP_THREADS=1
MODERNBERT_PREFILTER_ENABLED=false
MODERNBERT_PREFILTER_TOP_K=3

# =============================================================================
# 6. AI / ML Models
# =============================================================================
DENSE_MODEL_NAME=BAAI/bge-small-en-v1.5
SPARSE_MODEL_NAME=Qdrant/bm25
RERANK_MODEL_NAME=Xenova/ms-marco-MiniLM-L-6-v2
RERANK_TIMEOUT_SEC=2.0
VECTOR_SIZE=384
FASTEMBED_CACHE_DIR=./models/fastembed
HF_HOME=./models/huggingface
HF_TOKEN=

# =============================================================================
# 7. Infrastructure
# =============================================================================
TZ=Asia/Kuala_Lumpur
REDIS_URL=redis://localhost:6379/0
CELERY_BROKER_URL=redis://localhost:6379/1
CELERY_RESULT_BACKEND=redis://localhost:6379/2
QDRANT_URL=http://localhost:6333
COLLECTION_NAME=documents

# =============================================================================
# Worker Performance (optional)
# =============================================================================
CELERY_WORKER_MAX_TASKS_PER_CHILD=100
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING=250
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING=500
CELERY_WORKER_MAX_MEMORY_PER_CHILD=4194304
CELERY_EMBED_SOFT_TIME_LIMIT_SEC=900
CELERY_EMBED_TIME_LIMIT_SEC=1200
CELERY_TASK_REJECT_ON_WORKER_LOST=true

Configuration Best Practices

DEBUG=false
LOG_LEVEL=INFO
RATE_LIMIT=30
API_KEY_CLIENTS={"REPLACE_WITH_A_STRONG_RANDOM_KEY":"prod-client"}
CELERY_WORKER_MAX_TASKS_PER_CHILD=100
CELERY_WORKER_MAX_MEMORY_PER_CHILD=1572864 # 1.5GB

Docker Compose

Environment variables are automatically loaded from .env when using:

just prod-up

just prod-up also writes .runtime/prod.env with computed startup values such as API worker count and worker scale targets so operators can inspect the chosen runtime plan.

Or set in docker-compose.yml:

services:
api:
environment:
- API_KEY_CLIENTS=${API_KEY_CLIENTS}
- LOG_LEVEL=INFO
Testing Your Config

After making changes to .env, restart the services to apply them:

just stop
just dev

What's Next

Now that you understand the configuration options, you can:

If something is not working as expected, check the logs with LOG_LEVEL=DEBUG and review the troubleshooting section in the installation guide.