From dd8786f4148e7cb1b731c96b539209600dcf53bf Mon Sep 17 00:00:00 2001 From: Sebastian Danielsson Date: Thu, 20 Aug 2026 12:49:42 +0200 Subject: [PATCH] build: download nltk data somewhere a non-root UID can read `nltk.download` targets the first entry of `nltk.data.path` that already exists and is writable. None of the system-wide candidates exist in this image, so it falls back to `$HOME/nltk_data` -- `/root/nltk_data`, inside a directory that is mode 0700. The corpus itself is world-readable; the parent is what blocks it. So the baked `punkt_tab` is unreachable for any run that is not root, including `docker run --user`, `runAsNonRoot` and OpenShift's restricted SCC: $ docker run --rm --user 1002720000:0 open-webui:v0.11.0 \ python -c "import nltk; nltk.data.find('tokenizers/punkt_tab')" LookupError: Resource punkt_tab not found. `/usr/local/share/nltk_data` is already on `nltk.data.path`, so pointing the download there needs no NLTK_DATA and no change at the read side. Verified as `--user 1002720000:0`, where `nltk.data.find` now resolves it. Scope, to be straight about it: I could not find anything that currently loads this corpus -- `unstructured` 0.22.31 uses spaCy, and `retrieval.py` instantiates only the Markdown, RecursiveCharacter and Token splitters. So this makes 15 MB that ships either way actually usable, rather than fixing a reported failure. Happy to send a patch that drops the download instead if you prefer the smaller image. (`USE_PERMISSION_HARDENING=true` already fixes this for that build; no published tag sets it.) Co-Authored-By: Claude Opus 5 (1M context) --- Dockerfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 009081d9dd..8336a3e6e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -149,7 +149,9 @@ RUN set -e; \ python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ.get('AUXILIARY_EMBEDDING_MODEL', 'TaylorAI/bge-micro-v2'), device='cpu')"; \ python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \ python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \ - python -c "import nltk; nltk.download('punkt_tab')"; \ + # Without an explicit dir this lands in /root/nltk_data, which is mode 0700 + # and so unreadable when the container does not run as root. + python -c "import nltk; nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')"; \ else \ pip3 install 'torch<=2.9.1' torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir; \ uv pip install --system -r requirements.txt --no-cache-dir; \ @@ -158,7 +160,9 @@ RUN set -e; \ python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ.get('AUXILIARY_EMBEDDING_MODEL', 'TaylorAI/bge-micro-v2'), device='cpu')"; \ python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \ python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \ - python -c "import nltk; nltk.download('punkt_tab')"; \ + # Without an explicit dir this lands in /root/nltk_data, which is mode 0700 + # and so unreadable when the container does not run as root. + python -c "import nltk; nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')"; \ fi; \ fi; \ mkdir -p /app/backend/data; chown -R $UID:$GID /app/backend/data/; \