Installation¶
Two ways to run the app: Docker Compose (what you deploy) and a local dev
setup (what you develop against). Both read configuration from .env in the
repo root.
Docker Compose¶
Prerequisites: Docker (or Podman) with the Compose plugin. No GPU is needed unless you run the OCR sidecar.
git clone https://github.com/KatherLab/deidentifier.git
cd deidentifier
cp .env.example .env
$EDITOR .env # fill in the LLM block at the top
docker compose up -d --build
The app is at http://localhost:8080.
The one thing to configure is at the top of .env: the detection LLM. Point
OPENAI_API_BASE at any OpenAI-compatible endpoint (Ollama, vLLM, LM Studio,
a gateway) and set LLM_MODEL to a model it serves — see
LLM endpoints for model recommendations.
While those values are empty the backend refuses to start, and its log says
exactly that (Refusing to start: detector 'llm' is enabled but
OPENAI_API_BASE/LLM_MODEL are not set) — a deployment that silently misses
names would be worse. To look around without an LLM endpoint, set
DETECTORS=rules: the app then runs with no external services but finds only
structured identifiers, not names.
localhost in .env means the backend container
The most common first failure. A model server running on your machine is
not at localhost from inside the container — use
host.docker.internal or a compose service name. See
Configuration.
What that starts:
| Container | Role |
|---|---|
frontend |
nginx serving the SPA and reverse-proxying /api/ to the backend. The only published port (8080). |
backend |
FastAPI. No published port, read-only filesystem, no volumes — nothing is persisted. |
APP_ENV defaults to production in compose.yml, which disables the API
docs and refuses to start on unsafe configuration (mock detector enabled,
insecure content logging enabled, or the llm detector enabled without an
endpoint). If the backend container exits immediately, read its log: the
message starts with Refusing to start:.
Layered variants¶
# Development: source mounts + hot reload, backend exposed on :8000
docker compose -f compose.yml -f compose.dev.yml up --build
# Add a GPU OCR sidecar (vLLM) and wire it up — pick one:
docker compose -f compose.yml -f compose.unlimited-ocr.yml up -d # baidu/Unlimited-OCR
docker compose -f compose.yml -f compose.chandra.yml up -d # datalab chandra
The OCR overlays require an NVIDIA GPU and the NVIDIA Container Toolkit. Each
sets OCR_ENGINE=llm_vision and points the backend at its sidecar
automatically — see OCR engines.
Behind a reverse proxy¶
The app has no authentication by default: it is designed to sit behind the
institution's existing auth proxy. Put your proxy in front of the frontend
container, terminate TLS there, and do not publish port 8080 beyond it.
FRONTEND_PORT changes the published port.
If you have no such proxy, the app can require a sign-in at your organisation's OpenID Connect provider instead — see Single sign-on. You still need TLS in front of it.
Local development setup¶
Prerequisites: Python 3.13 or 3.14 (requires-python = ">=3.13,<3.15"),
uv, Node.js 24+.
(Development mode starts with the LLM block empty, but every request then fails with a clear 503 instead of a result.)
Two terminals:
uv run uvicorn backend.src.main:app --reload --host 0.0.0.0 --port 8000
npm run dev # → http://localhost:5173
The dev frontend talks to http://localhost:8000 directly, so that origin must
be listed in APP_CORS_ORIGINS (the shipped default covers ports 3000 and
5173).
Verify the install:
Upgrading¶
There is no database and no migrations, so an upgrade is a rebuild. Check
CHANGELOG.md
for configuration changes and compare your .env against the current
.env.example — new variables always have safe defaults, but defaults change.
In-flight results live in memory only: restarting the backend drops any cached detection, and users with an open result get a "please re-run" message rather than a broken page.