Deployment¶
The stack¶
| Service | Image | Notes |
|---|---|---|
frontend |
ghcr.io/katherlab/deidentifier-frontend |
nginx (unprivileged) serving the SPA and proxying /api/ to the backend. The only published port. |
backend |
ghcr.io/katherlab/deidentifier-backend |
FastAPI. No published port, read-only root filesystem, tmpfs for /tmp, no volumes. |
Both run as non-root. DEIDENTIFIER_IMAGE_TAG pins a version;
FRONTEND_PORT changes the published port.
nginx re-resolves the backend hostname per request, so recreating the backend container does not require restarting the frontend.
Production mode¶
compose.yml sets APP_ENV=production by default, which:
- disables
/docsand/openapi.json, - refuses to start when the mock detector is enabled, when
APP_ALLOW_INSECURE_CONTENT_LOGGINGis true, or when thellmdetector is enabled withoutOPENAI_API_BASE/LLM_MODEL.
A backend that exits immediately after docker compose up almost always logs
Refusing to start: … with the exact reason.
Authentication¶
There is none, by design: the app is meant to run behind the institution's existing authenticating reverse proxy. Terminate TLS there, enforce authentication and authorization there, and do not expose port 8080 beyond it.
Everything a user can reach is a stateless endpoint that processes the document they submitted — there are no accounts, no stored documents, and nothing to enumerate. That is only true as long as the proxy is actually in front of it.
Configuration¶
Read from .env in the repo root at runtime, never baked into an image:
backend/.env is still read as a fallback, with .env and the container
environment taking precedence. See Configuration.
Layered compose files¶
# Development: source mounts + reload, backend published on :8000
docker compose -f compose.yml -f compose.dev.yml up --build
# GPU OCR sidecar (baidu/Unlimited-OCR via vLLM), wired up automatically
docker compose -f compose.yml -f compose.unlimited-ocr.yml up -d
Health and monitoring¶
| Endpoint | Purpose |
|---|---|
GET /health/live |
Process is up. Used by the container healthcheck. |
GET /health/ready |
Configured backends are reachable. |
GET /api/v1/status |
Detectors, OCR engine, endpoint hosts and their locality, limits. |
/api/v1/status deliberately returns hosts, not URLs, and never keys or
paths — it is what the frontend uses to raise the "content leaves this machine"
banner, so it must stay safe to expose.
Logs go to stdout in the usual container fashion. They contain request ids,
timings, character counts, entity counts, and validation status — never
document content. Do not enable APP_ALLOW_INSECURE_CONTENT_LOGGING on a
system that processes real data; production mode refuses to start with it.
Upgrading¶
No database, no migrations. Check CHANGELOG.md for configuration changes and
diff your .env against .env.example. Restarting drops in-flight
results: users with an open result see a "please re-run" message.
Backup¶
There is nothing to back up except your configuration — that is the design.
Keep your .env in your usual secret store; everything else is in the
repository.