From zero to a compiled document.
nautris is self-hosted: it runs entirely in your infrastructure as a Docker image. This page takes you from an empty machine to parsing your first document.
On this page
Deploy with Docker
Pull the engine image straight from our registry, no account or login required, then deploy with a compose file. The image is public; the engine still needs a valid license to run (it refuses to start without one), so the license, not the registry, is the gate.
docker pull registry.nautris.com/nautris:latest
# docker-compose.yml - one image, two roles: API + N workers on a shared volume
services:
api:
image: registry.nautris.com/nautris:latest
restart: unless-stopped
ports: ["8000:8000"]
environment:
NAUTRIS_LICENSE: ${NAUTRIS_LICENSE:?required}
NAUTRIS_WORKERS: "0" # the API enqueues...
NAUTRIS_BACKGROUND: "1" # ...the worker containers process
NAUTRIS_RAG_DB: /data/lancedb
volumes:
- engine-data:/data
worker:
image: registry.nautris.com/nautris:latest
command: ["python", "-m", "nautris.worker"]
restart: unless-stopped
environment:
NAUTRIS_RAG_DB: /data/lancedb
volumes:
- engine-data:/data
mem_limit: 8g # isolates a heavy doc to one worker; large OCR PDFs peak past 4g
volumes:
engine-data:
The published image (registry.nautris.com/nautris:latest) is ~6-7 GB: it carries the CUDA torch build so the one image runs on both CPU and GPU hosts. No GPU is required; it falls back to CPU. (Building from source with the default CPU wheels yields a slim ~2.7 GB image if you know you will never use a GPU.) Extraction models are fetched once on first run into the shared volume. Scale extraction with docker compose up --scale worker=4.
System requirements
No GPU is required: the core path is fully CPU-deterministic, and a GPU only accelerates the same OCR when one is present. What actually matters:
| Resource | Minimum | Comfortable | What consumes it |
|---|---|---|---|
| Disk | ~12 GB free | 20 GB+ | image ~6-7 GB (universal) + model cache ~1.3 GB, fetched on first job + job artifacts |
| RAM | ~8 GB (1 worker) | 16 GB+ | extraction peaks at 3–6 GB per worker (more on large / OCR-heavy PDFs); the API process is light. Budget per-worker RAM × worker count |
| CPU | 2 cores (any); AVX2 + FMA for OCR | 4+ cores, AVX2 + FMA | Born-digital PDFs use the model-free fast path and run on any CPU - older x86, ARM, Apple Silicon. The ML / OCR path (scanned pages, complex layouts) needs AVX2 + FMA on x86: its runtime (onnxruntime/torch) is built for them, so Intel Haswell (2013)+ / AMD Zen (2017)+ is the floor there; ARM/Apple Silicon run it fine without AVX2. On an x86 chip without AVX2 the ML path is unavailable and those documents fail cleanly with a clear error (as of engine 0.9.2 - no longer a worker crash). Above the floor, cores are throughput only: native-text pages are fast, OCR pages run ~20–90 s/page |
| GPU | optional | The published image runs on both: GPU-accelerated where an NVIDIA GPU + nvidia-container-toolkit exist (10-30x faster on scanned / OCR-heavy PDFs, identical output), CPU otherwise. No separate image; give the worker a GPU reservation to use it | |
Measured throughput: born-digital documents extract at roughly 280-300 pages/min on a single GPU worker (a 150-page report finishes in about 32 s); full GPU OCR on image-heavy scans runs around 20 pages/min. On the published benchmark corpus the engine scores 100% on tables, reading order, and headings.
When resources run short, failures stay contained and visible, never silent corruption:
- Disk full: image pulls fail loudly; a mid-job failure retries, then lands in the dead-letters view in
/uiwith its error. Free space, hit Requeue. - Out of memory: under Docker Compose each worker is fenced by
mem_limit(8 GB in the example above; raise it for large OCR-heavy PDFs, which can peak past 4 GB), so a heavy document kills only that worker (exit 137) and the job dead-letters while the API and UI stay up. Bare pip installs have no fence, so prefer Compose on small machines. - Slow CPU: never fatal.
POST /v1/parsedegrades to202 + job IDafter ~55 s and the queue keeps working; pollGET /v1/jobs/{id}. - x86 CPU without AVX2/FMA: no longer fatal (as of engine 0.9.2). Born-digital documents still extract via the model-free fast path. Documents that need the ML pipeline (scanned / OCR / complex layouts) fail cleanly with a clear
ml-unavailableerror instead of crash-looping the worker (older versions aborted with SIGILL / exit132). To process those, run the engine on a newer x86 host (AVX2), an NVIDIA GPU, or ARM/Apple Silicon (which run the ML path without AVX2). Check x86 support withgrep -qw avx2 /proc/cpuinfo && echo ok || echo "no AVX2: born-digital only". - Oversized inputs: uploads stream against a hard cap (413 past 50 MB by default) instead of buffering into memory; workers run with pids/memory caps and a read-only rootfs.
Quickstart
A valid license is required: the server refuses to start without one (personal licenses are free and instant at get.nautris.com). There are no API keys to invent: the engine mints a default token on first boot, prints it in the startup logs, and shows it in /ui.
export NAUTRIS_LICENSE="nl1...." # free personal license
docker compose up -d
docker compose logs api | grep "Default API token"
# Default API token: dek_...
Parse a document and search it (send the token as X-API-Key):
# parse one file, get the canonical tree back in a single call
curl -X POST localhost:8000/v1/parse -H "X-API-Key: dek_..." -F "file=@mydoc.pdf" | jq .tree
# semantic search across everything indexed
curl -G localhost:8000/v1/search -H "X-API-Key: dek_..." --data-urlencode "q=your question" | jq '.[0]'
Then open the review dashboard at http://localhost:8000/ui. It signs itself in with the default token automatically; view, copy, or rotate the token on its API token page.