Repository navigation
Settings
This page documents every configurable setting, its default, and — most importantly — its caveats: the non-obvious behaviours that surprise people (scores that aren't what they look like, changes that silently do nothing until you re-index, knobs that trade recall for latency, and options with hidden dependencies or cost).
It is organised to mirror the Settings page in the app. Each setting lists its
config key (as stored in the database settings row) so you can cross-reference logs
and API payloads.
Source of truth: the authoritative schema is
backend/models.py→PaperlessIQConfig. If this page and the code ever disagree, the code wins — please open a PR to fix the doc.
Some settings change how documents are embedded and stored. Changing them does not retroactively rewrite the existing index — your old vectors stay as they were, and only newly-indexed documents use the new value, until you rebuild. Until then you get an inconsistent or silently-degraded index.
After changing any setting marked 🔁 Requires re-index, go to Processing → Re-index Vector Store.
Settings that require a re-index: chunk_size, chunk_overlap, chunk_strategy,
the embedding model/provider, qdrant_hybrid_search, qdrant_quantization,
HNSW build params (*_hnsw_m, *_construction_ef). Query-time params
(*_hnsw_ef / *_search_ef, search_min_score, search_overfetch_multiplier,
rerank settings) apply live.
This is the single most misunderstood setting. search_min_score (UI: Min Score)
filters results by a score in [0, 1], but what that number represents depends on
the active mode:
| Mode | What the score is | Practical meaning |
|---|---|---|
| Dense only (no hybrid, no rerank) | (cosine + 1) / 2 |
Inflated: unrelated (cos 0) → 0.5, so even loose matches score ~0.6–0.8. A 0.45 threshold is very permissive. |
| Hybrid on, no rerank (a common default) | RRF fusion score, min-max normalised across that one query's results |
Relative, not absolute. The best hit in each search is always 1.0, the worst 0.0. 0.45 means "≈45% of the way from the weakest to the strongest candidate in this batch" — not "45% relevant." |
| Reranker on (any fusion) | The reranker's own score (overrides the above) | LLM reranker → rating 0–10 ÷ 10, i.e. quantised to {0.0, 0.1 … 1.0} (a 0.45 cutoff ≈ "keep ≥ 5/10"). Local cross-encoder → calibrated sigmoid(logit). |
Consequences to know:
- In hybrid, no-rerank mode the top result is always ~1.0 even when nothing in the
archive is actually relevant — so
min_scorecannot gate out a "best of a bad bunch." The number gives false confidence about absolute relevance. - Raising
min_scorein hybrid mode does not demand "better" matches; it just trims more of the lower-ranked candidates relative to that query's result set. - The thing that judges absolute relevance is the reranker (see
Re-ranking). If results feel insufficiently relevant, enable reranking
rather than chasing a higher
min_score. - Implementation:
vector_store.py→_point_scores.
The in-app tooltip currently calls this a "normalised cosine score," which is only accurate in the dense-only case. Treat the table above as the real behaviour.
Paperless NGX connectivity and callback URLs. The credentials live in the environment, not the settings database (see Environment variables).
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Webhook secret | webhook_secret |
"" (auto-generated on first run) |
Stored encrypted. Authenticates the Paperless NGX webhook callback. Empty = no auth required (not recommended if reachable beyond localhost). |
| Public Paperless URL | paperless_public_url |
"" |
The URL your browser can reach Paperless at. PAPERLESS_URL (env) is the internal Docker address; deep-links in Discovery are rewritten from internal → public using this. Set it if internal and public hostnames differ. |
| Paperless IQ internal URL | paperless_iq_internal_url |
"" |
URL of Paperless IQ as reachable from Paperless NGX (used when registering the webhook). Leave empty to derive it from the incoming request. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Provider | llm_provider |
— (required) | One of ollama, bedrock, anthropic, openai. |
| Model | llm_model |
provider-specific | Per-provider defaults: ollama llama3, bedrock claude-3-haiku, anthropic claude-3-5-haiku, openai gpt-4o-mini. |
| Credentials | llm_credentials |
— |
Fernet-encrypted, never returned to the UI. The __KEEP__ sentinel means "keep the stored value." |
| Ollama URL | ollama_url |
http://localhost:11434 |
Only used when provider is ollama. |
| LLM timeout (s) | llm_timeout_seconds |
120 |
Max seconds to wait for an LLM response. 0 = no limit. Applies to analysis, discovery answers, and the LLM reranker. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Backend | vector_store_backend |
local |
local = ChromaDB (in-container, zero setup). qdrant = external Qdrant service (better at scale, supports hybrid search + quantization). bedrock_kb = AWS Bedrock Knowledge Base (managed; AWS does its own retrieval). |
| Bedrock KB ID | bedrock_kb_id |
null |
Required when backend is bedrock_kb. |
Switching backends does not move your vectors. Use Processing → Migrate
embeddings (or POST /api/vector/migrate) to copy existing vectors across without
re-embedding — but only if the embedding model is unchanged. If the model changed, a
full re-index is required.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Mode | qdrant_mode |
local |
local = self-hosted (compose service). cloud = Qdrant Cloud (set URL + API key). |
| URL | qdrant_url |
http://qdrant:6333 |
Default is the compose service DNS name. |
| API key | qdrant_api_key |
"" |
Fernet-encrypted, never returned to the UI. Needed for Qdrant Cloud. |
| Collection | qdrant_collection |
paperless_iq_chunks |
Document-chunk collection name. |
| Memory collection | qdrant_memory_collection |
piq_memories |
Long-term memory collection name. |
These next three groups live in the Vector Store section of the page (they describe how the index is built and stored). Query-time relevance knobs are under Similarity Search Tuning.
How documents are split before embedding. 🔁 Changing any of these requires a re-index.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Chunk size (chars) | chunk_size |
1000 |
Max characters per embedded chunk. Smaller = more precise retrieval; larger = more context per chunk. |
| Chunk overlap (chars) | chunk_overlap |
200 |
Characters shared between consecutive chunks, so answers straddling a boundary are still retrieved. |
| Chunk strategy | chunk_strategy |
char |
char = fixed-width windows (fast). sentence = packs whole sentences up to chunk_size, avoiding mid-sentence cuts. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Search ef | chroma_hnsw_search_ef |
100 |
Query candidate-list size. Higher = better recall, more latency. Must be ≥ similar_docs_count × search_overfetch_multiplier or recall silently caps (validator-enforced). Applied live. |
| M (graph connectivity) | chroma_hnsw_m |
16 |
Links per node. Higher = better recall, more memory. 🔁 Build-time — requires re-index. |
| Construction ef | chroma_hnsw_construction_ef |
100 |
Index build quality. Higher = better recall, slower indexing. 🔁 Build-time — requires re-index. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| HNSW ef | qdrant_hnsw_ef |
128 |
Query-time candidate list. Higher = better recall, more latency. Must be ≥ similar_docs_count × search_overfetch_multiplier (validator-enforced). Applied live. |
| M | qdrant_hnsw_m |
16 |
Graph connectivity. 🔁 Build-time — requires re-index. |
| Quantization | qdrant_quantization |
none |
none = full precision. scalar = INT8, ~4× smaller, minimal quality loss. binary = ~32× smaller, noticeable quality loss. 🔁 Requires re-index.
|
Used for semantic search and smart entity selection. Can use a different provider than the chat LLM — embedding models are small and fast.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Embedding provider | embed_provider |
ollama |
ollama, bedrock, or openai. |
| Embedding model | embedding_model |
nomic-embed-text |
Per-provider defaults: ollama nomic-embed-text, bedrock amazon.titan-embed-text-v1, openai text-embedding-3-small. 🔁 Changing the model requires re-indexing — the vector dimension must match the index, or search fails with a dimension-mismatch error. |
| Parallel embeddings | embed_concurrency |
1 |
Embedding API calls in flight at once. For Ollama this is chunks-per-document; for cloud providers it now also overlaps whole documents during indexing. 1 is safe for a local Ollama. Raise to 4–8 for a remote/GPU Ollama, and to ~8 for Bedrock (the Bedrock embeddings panel defaults this to 8). The vector store's embed semaphore caps total concurrent calls, so raising it never exceeds this number regardless of how many documents are being indexed. |
| Embedding batch size | embed_batch_size |
32 |
Texts sent per embedding API call. Only Cohere Embed models on Bedrock batch multiple texts in one call (up to 96) — this cuts request count and cost during indexing. Titan and all non-Bedrock providers ignore it and always send one text per call. |
| Embedding endpoint | embed_base_url |
(empty) | Point embeddings at a different server than the chat model — a GPU box, a local vLLM, a separate OpenAI-compatible host. Empty = inherit the LLM section's endpoint. 🔁 Changing this requires re-indexing: the same model name served by a different host produces incompatible vectors, and mixing two vector spaces in one collection degrades retrieval with no error. |
| Embedding API key | embed_credentials |
(empty) | A key just for the embedding endpoint. Empty = reuse the LLM's key, which only applies when the LLM provider is compatible (an OpenAI-style embed role never inherits Bedrock credentials). Self-hosted endpoints such as vLLM usually need no key. Rotating this does not trigger a re-index — same endpoint, same vectors. |
Mixing providers is now supported. Earlier versions required
llm_provider = openaibeforeembed_provider = openaiwould work, because both roles shared one provider instance. Each role now resolves independently (D-27), so a local Ollama chat model with OpenAI-compatible embeddings on another host is a valid configuration.
Controls when metadata changes re-embed a document. A re-embed happens after an
approval writes new metadata, after a webhook edit, and after a grooming merge —
because chunk embeddings carry a metadata prefix (tags/correspondent/type), so changing
those should refresh the vector. First-time indexing always embeds immediately and is
unaffected. The chokepoint is schedule_reembed(); only re-embeds of already-indexed
documents are deferred.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Refresh mode | embed_refresh_mode |
immediate |
immediate = re-embed the moment metadata changes (vectors always fresh; default, zero behaviour change). daily = stamp the document dirty and re-embed all dirty docs once per day. manual = stamp dirty and wait for the user to flush. Daily/manual debounce repeated same-day edits (one re-embed, not N) and spread embedding cost — useful on metered embedding APIs or after a large grooming merge. The dirty document re-fetches its current content + metadata from Paperless at flush time, so the deferred vector reflects the latest state, not the state when it was stamped. |
| Daily flush hour (UTC) | embed_refresh_hour |
3 |
0–23. Only used when mode = daily. The flush loop checks every minute and runs when the UTC hour matches. |
| Content-drift reindex (days) | content_drift_reindex_days |
7 |
Safety net for content/OCR edits and tag removals that didn't fire the webhook. Every N days: (1) documents whose Paperless modified is newer than our last embed are re-embedded, and (2) every document's current tags are compared against the tags it had at its last embed — a mismatch re-embeds it too. 0 disables both. This second check exists because Paperless NGX's bulk tag edit (remove_tag/modify_tags) and deleting a tag outright neither fire the webhook nor bump modified, so they're invisible to (1) and to the webhook alike. The webhook remains the primary, real-time path for everything else — this only catches drift it missed. First run is one interval after startup. Compares against the per-document last_embedded_at/last_tag_ids_json, so it never re-embeds an unchanged document. |
When dirty re-embeds are pending (daily/manual), the count is exposed at
GET /api/embeddings/pending; POST /api/embeddings/refresh flushes them now (the UI
surfaces a banner). See D-22 for why merges defer rather than re-embed inline.
Embed visibility. Every document embed writes an
embeddedevent to the audit log (with the document title and achange_sourcenaming the trigger:system:index,approval,webhook,system:flush,drift). This makes double-embeds visible — e.g. a freshly added document fires the webhook and gets picked up for analysis → embedded on approval. Filter the Audit page by theembeddedaction (and by source) to see the history; old rows are pruned byaudit_retention_days.
Query-time relevance knobs — all applied live (no re-index). Index-build settings (chunking, HNSW) live in the Vector store section instead.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Overfetch multiplier | search_overfetch_multiplier |
5 |
Candidates fetched = top_n × this. Higher = better recall, more post-processing. Also feeds the HNSW ef validation. |
| Min Score | search_min_score |
0.0 (off) |
Drop results below this [0,1] score. 0 disables the filter. |
| Hybrid search | qdrant_hybrid_search |
false |
(Qdrant only.) Combines dense (semantic) + sparse (BM25 keyword) vectors via RRF fusion — improves recall for precise/rare-term queries (names, IDs, exact phrases). It's a query strategy, so it lives here rather than with the index settings. Caveats: ① needs the fastembed sparse encoder (paperless-iq[qdrant-hybrid] extra). ② 🔁 Requires re-index to build the sparse vectors. ③ changes the meaning of the score to relative min-max — see the score caveat. |
After vector search, re-score (query, passage) pairs to significantly improve
precision. This is the real lever for relevance quality (more so than min_score).
Ships OFF. Adds latency proportional to rerank_top_k × passage length.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Enable re-ranking | rerank_enabled |
false |
Master switch. When on, the reranker's score overrides the vector/fusion score everywhere. |
| Reranker | rerank_method |
llm |
See method comparison below. |
| Re-rank top K | rerank_top_k |
20 |
Candidates passed to the reranker. Higher = better recall before reranking, more reranker latency. |
| Model | rerank_model |
BAAI/bge-reranker-v2-m3 |
HuggingFace ID (for local), Bedrock model ID/ARN (for api), or the model name the rerank server expects (for cohere_api). The default is multilingual (~560 MB, downloaded on first use). TEI ignores this field — it serves whichever model it was started with. |
| Rerank endpoint | rerank_base_url |
(empty) | Base URL of the rerank server, without the path — Paperless IQ appends /v1/rerank for cohere_api and /rerank for tei. Required for both HTTP methods; empty falls back to the OpenAI base URL when the LLM provider is OpenAI. |
| Rerank API key | rerank_api_key |
(empty) | Sent as a Bearer token. Empty = reuse the LLM API key (OpenAI/Anthropic only), or send none at all. Self-hosted vLLM and TEI usually need no key. |
Method comparison:
| Method | Dependencies | Cost / latency profile | Caveats |
|---|---|---|---|
llm (your chat LLM, listwise) |
None — reuses LLM creds | One extra LLM call per query | Scores are quantised to rating/10. Cheapest to operate; recommended default. If automation is on, every suggestion also reranks → extra LLM load. |
local (in-process cross-encoder) |
paperless-iq[rerank-local] (sentence-transformers + torch) |
CPU-bound and heavy | rerankers.py), but on a busy box it still adds seconds per query. The image bundles a CPU-only torch build (PIQ_EXTRAS includes rerank-local) — see Environment variables. First use downloads the weights into HF_HOME (persisted). |
api (Amazon Bedrock Rerank) |
Bedrock as the LLM provider | Per-call AWS cost | Requires llm_provider = bedrock. Enter a Bedrock rerank model ID/ARN (e.g. amazon.rerank-v1:0, cohere.rerank-v3-5:0). |
cohere_api (rerank API) |
A reachable rerank endpoint | One HTTP call per query | Speaks the Cohere rerank format, which Cohere, Jina, vLLM and Infinity all share: POST {base}/v1/rerank. The best option for a self-hosted GPU reranker — no local CPU cost, no torch dependency. |
tei (Hugging Face TEI) |
A reachable TEI endpoint | One HTTP call per query | Text Embeddings Inference is not Cohere-compatible despite often being described as such — it uses POST {base}/rerank with a different body and a bare-array response. Pick this rather than cohere_api for a TEI server, or every request will fail. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Default analysis mode | default_analysis_mode |
ocr |
ocr = send the existing OCR text (fast, cheap). full_document = send the rendered document (vision models; higher quality, higher cost). |
| Per-doctype analysis mode | per_doctype_analysis_mode |
{} |
Override the mode per document type (id → ocr/full_document). |
| Context window (chars) | context_window_chars |
128000 |
Max characters sent to the LLM; content beyond this is truncated. Keep ≤ your model's context window. |
| Vision page warning | vision_max_pages_warning |
5 |
When full_document is used and a doc exceeds this page count, the UI warns (Keep / Limit / Cancel) before incurring vision cost. |
Hybrid strategy for suggesting tags/correspondents/types: vector similarity to past documents, with a frequency-based fallback.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Smart entity selection | smart_entity_selection |
true |
When off, falls back to frequency only. |
| Similar documents to consider | similar_docs_count |
10 |
How many similar docs feed entity suggestions. Also one factor in the HNSW ef validation. |
| Frequency fallback count | frequency_fallback_count |
20 |
Top-N most frequent entities offered when similarity yields little. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Tag policy | tag_creation_policy |
existing_only |
existing_only = LLM may only pick from existing tags. allow_new = LLM may propose new ones. |
| Correspondent policy | correspondent_creation_policy |
existing_only |
As above, for correspondents. |
| Document-type policy | doctype_creation_policy |
existing_only |
As above, for document types. |
| Storage-path policy | storage_path_creation_policy |
existing_only |
As above, for storage paths. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Global prompt template | global_prompt_template |
built-in classifier prompt | The base instruction sent for metadata analysis. |
| Per-field prompt templates | per_field_prompt_templates |
{} |
Override the prompt for a specific field. |
| Per-doctype prompt templates | per_doctype_prompt_templates |
{} |
Override the prompt per document type. |
| Discovery system prompt | discovery_system_prompt |
null (built-in default) |
The system prompt body for the Discovery (RAG) answer. |
| Field descriptions | field_descriptions |
{} |
Per-field instructions telling the LLM how to populate each metadata field. |
The background loop that analyses (and optionally applies metadata to) incoming documents.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Automation enabled | automation_enabled |
false |
Master switch for the background loop. Caveat: when on, it continuously runs analysis and embedding/entity search — which means it also exercises the reranker continuously. With rerank_method = local this keeps the CPU busy and can starve interactive Discovery. |
| Inbox tag | inbox_tag_id |
null |
The tag that marks "needs processing." Documents with it are picked up; suggestions exclude already-tagged (reviewed) docs. |
| Auto-apply | auto_apply |
false |
When on, every suggestion the poller/scheduler produces is approved automatically and immediately — nothing waits for a human, and entities allowed by the creation policies are created in Paperless at that moment (audited as change_source=automation). Off = suggestions wait in the queue for human approval, and nothing is created until you approve. |
| Poll interval (s) | poll_interval_seconds |
10 |
How often the inbox poller checks for new documents. Minimum 1 (validator-enforced). |
| Batch size | batch_size |
10 |
Documents processed per scheduled batch run. Minimum 1 (validator-enforced). |
| Schedule (cron) | schedule_cron |
null |
Cron expression for scheduled batch analysis (separate from the always-on inbox poller). A real cron schedule (croniter) — e.g. 0 2 * * * = 2 AM daily. Empty/null disables it. Invalid expressions are rejected on save (422). Edits apply within ~30s, no restart. |
Vocabulary maintenance — entity descriptions, duplicate detection, and a similarity
mismatch scan that suggests tag/correspondent/type corrections. The whole feature
is off by default and gated behind a dedicated can_groom permission. The Library
page is hidden for everyone (admins included) until grooming_enabled is on. The scan
is zero-LLM / zero-embed (it reuses stored entity vectors); the only LLM spend is
description generation. See D-21/D-22/D-23 for the design decisions.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Enable grooming | grooming_enabled |
false |
Master switch. Off = Library page hidden, scheduled scans idle. |
| Entity types | grooming_entity_types |
["tag","correspondent","document_type"] |
Which entity kinds the Library manages. |
| Dedup — name threshold | grooming_dedup_name_threshold |
0.85 |
[0,1] fuzzy-name similarity to flag a name-duplicate pair (LLM-free). |
| Dedup — embedding threshold | grooming_dedup_embed_threshold |
0.90 |
[0,1] cosine between entity vectors to flag a semantic-duplicate pair. Only fires for entities that have a generated description (hence an embedding). |
| Description — sample docs | grooming_desc_sample_docs |
5 |
How many representative documents are sampled to generate an entity's description. |
| Description — snippet chars | grooming_desc_snippet_chars |
300 |
Characters of content per sampled doc included in the generation prompt. |
| Scan — add threshold | grooming_add_threshold |
0.80 |
Doc similarity at/above which the scan suggests adding the entity. Must be > remove threshold (validator-enforced — the dead band between them is the hysteresis that prevents flip-flopping). |
| Scan — remove threshold | grooming_remove_threshold |
0.35 |
Doc similarity below which an applied tag is suggested for removal. |
| Scan — remove percentile | grooming_remove_percentile |
10 |
Cohort-relative rule: a doc in the bottom N% of its entity's cohort can be flagged even if it clears the absolute remove threshold. Counters the D-18 prefix inflation on removals. Cohort scoring needs a filterable backend — Qdrant for tags; both backends for correspondent/document_type. On Chroma, tag cohorts fall back to the absolute rule only. Needs a cohort of ≥10 docs to engage. |
| Scan — min supporting chunks | grooming_min_supporting_chunks |
2 |
An add needs at least this many chunks above the add threshold — one lucky chunk isn't enough. |
| Scan — top-K | grooming_scan_top_k |
100 |
Chunks retrieved per entity vector query. Also gates the "absence is evidence" remove rule (only when top_k ≥ 3 × entity_doc_count). |
| Scan — max suggestions / run | grooming_max_suggestions_per_scan |
50 |
Hard cap on documents enqueued per scan, highest ` |
| Scan schedule (cron) | grooming_scan_cron |
null |
Cron for scheduled scans (croniter; e.g. 0 3 * * *). Empty/null = manual-only (Library → Scan → Scan now). Invalid expressions rejected on save. Scheduled scans are incremental — only entities that are new, whose description changed, or whose documents were re-embedded since their last scan (content drift) are re-examined; the manual Scan now is always a full scan. The scan is hard-disabled on the bedrock_kb backend (no vector queries) and skips while the embed circuit breaker is open. |
| Re-suggest after (days) | grooming_resuggest_after_days |
0 |
0 = a rejected suggestion is never re-suggested (rejection = permanent answer, D-23). >0 = a dismissal expires after N days and may resurface. |
Dry run first. Embedding-model and corpus differences make universal thresholds impossible. Use Library → Scan → Dry run to preview scored candidates without enqueueing, and tune
grooming_add_threshold/grooming_remove_thresholdagainst your actual data before turning on a scheduled scan.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Sync NGX admins | sync_ng_admins |
true |
Paperless NGX superusers/staff automatically get full Paperless IQ access. Per-user permissions (can_access, can_view_queue, can_approve, can_analyze, can_discover, can_settings, can_groom) are managed in this tab's user list, not in this config blob. can_groom is additionally gated by grooming_enabled — even an admin can't see the Library when grooming is off. |
Authentication is required: Paperless IQ refuses to start unless
PAPERLESS_URL(env) is set, because login validates against Paperless (there is no open mode — see D-24). ThePAPERLESS_TOKENlives only in the environment and never in the settings DB.
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Memory enabled | memory_enabled |
true |
When on, relevant stored memories are retrieved and injected into Discovery answers (only those above a relevance threshold). Memory CRUD lives in the Memories tab. |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Audit retention (days) | audit_retention_days |
180 |
How long change-history rows are kept. Minimum 30 (validator-enforced). |
| Setting | Key | Default | Notes & caveats |
|---|---|---|---|
| Target language | target_language |
null |
Forces the language of LLM-generated output (titles, Discovery answers). Empty = match the user's input language. |
Theme settings (Appearance tab). These are purely cosmetic and apply live.
| Key | Default | Notes |
|---|---|---|
mantine_color |
teal |
Mantine primary palette name. |
color_scheme |
dark |
light / dark / auto. |
theme_primary_color |
#1a7288 |
Primary accent. |
theme_sidebar_from / theme_sidebar_to
|
#0a3344 / #0e4458
|
Sidebar gradient. |
theme_font / theme_font_size
|
Roboto / 14px
|
Base typography. |
theme_text_color / theme_bg_color / theme_card_color
|
#2d3239 / #f8f9fb / #ffffff
|
Core colours. |
theme_card_alt_hex / theme_card_alt_opacity
|
#1a7288 / 12
|
Alternate card tint. |
theme_chip_color |
"" |
Empty = derive from theme_primary_color. |
theme_nav_icons |
{} |
Per-nav-item icon overrides. |
These are set in docker-compose.yml / the container environment, not the settings
database. Several are deliberately kept out of the DB for security.
| Variable | Purpose | Caveats |
|---|---|---|
PAPERLESS_URL |
Internal Paperless NGX address (Docker network). | Required — the app refuses to start without it (login validates against Paperless; there is no open mode, see D-24). |
PAPERLESS_TOKEN |
Paperless NGX API token. | Environment only — never stored in the settings DB. Required for Paperless operations; if absent the app still starts (auth enforced) but warns loudly that search/writes/indexing will fail. |
SECRET_KEY |
Fernet key for encrypting credentials. | Auto-generated on first run into /data/.secret_key. Set explicitly if you need to restore an encrypted DB backup after a volume loss. |
DATABASE_URL |
SQLite path. | Defaults to /data/paperless_iq.db in the volume. |
PIQ_VECTOR_STORE_BACKEND |
Overrides the vector backend at boot. | Optional; the UI setting normally governs this. |
PIQ_QDRANT_URL |
Qdrant URL override. | Defaults to the compose service DNS. |
PIQ_EXTRAS |
Build arg selecting optional dependency extras baked into the image. | Default qdrant-hybrid,rerank-local. Controls whether hybrid search (fastembed) and the local reranker (sentence-transformers + CPU-only torch) are installed. Set to qdrant-hybrid (or empty) for a leaner image. Changing it requires a rebuild. |
HF_HOME |
HuggingFace cache dir. | Set to /data/hf-cache so reranker/sparse-encoder weights are downloaded once and survive restarts. |
-
"I raised Min Score but bad results still show / good ones disappeared." → In
hybrid mode the score is relative per query; in dense mode it's an inflated
(cos+1)/2. See the score caveat. Use a reranker for real relevance gating. - "I changed chunk size / embedding model / hybrid / quantization and nothing changed." → Those need a re-index (Processing → Re-index Vector Store).
-
"Discovery hangs / is very slow." → Likely
rerank_method = localon a CPU box, especially with automation also running. Switch tollmrerank, or accept the latency. -
"My image is huge (~6 GB)." → A CUDA torch build. Ensure the CPU-only torch path is
used (
PIQ_EXTRAS+ the Dockerfile CPU-wheel install); a correct build is ~1.7 GB. - "Search errors with a dimension mismatch." → The embedding model changed after indexing. Re-index.