Docker (all-in-one)
The fastest way to get Classifyre running: one image carrying the whole stack — the API, the web UI, the Python scan workers, and PostgreSQL — with nothing to provision and nothing to connect.
docker run -d --name classifyre \ -p 3000:3000 \ --shm-size=1g \ -v classifyre-pgdata:/var/lib/postgresql/data \ -v classifyre-data:/var/lib/classifyre \ -v classifyre-uv-cache:/cache/uv \ classifyre/all-in-one:0.5.6Then open localhost:3000. The first boot initialises the database and creates a workspace, which takes a few minutes on a laptop.
This is also a reasonable way to run Classifyre for a single investigator. For a team, or for anything that has to survive a machine dying, use the Helm chart instead — it runs the same images as separate, independently scalable workloads.
What is inside the image
| Process | Port | What it is |
|---|---|---|
| Caddy | 3000 | the only published port; routes to the two servers below |
| Web UI | 3100 (internal) | Next.js, and this documentation at /docs |
| API + worker | 8000 (internal) | REST, WebSockets, job queues, embeddings, and the parent of every scan |
| PostgreSQL 18 + pgvector | 5432 (internal) | all data |
They are supervised by s6-overlay,
which restarts anything that dies and makes docker stop and docker restart
behave as expected. Only port 3000 is reachable — PostgreSQL binds to loopback
inside the container’s own network namespace and is never exposed.
The scan workers are not a service: the API starts one process per scan and it exits with the scan, the same lifecycle Kubernetes gives them as Jobs.
The image ships a small embedding model (Xenova/all-MiniLM-L6-v2) already
baked in, so semantic search and duplicate review work offline from the first
scan.
Requirements
| Memory | 4 GB minimum, 8 GB recommended. The container refuses to start below 4 GB rather than failing halfway through your first scan. |
| CPU | 2 cores minimum. Scans are CPU-bound. |
| Disk | ~10 GB for the image and database, plus room for what you scan. |
| Shared memory | --shm-size=1g. Without it, large queries fail with could not resize shared memory segment. |
On macOS and Windows, Docker Desktop caps what containers may use and often defaults to 2 GB — below the floor. Raise it under Settings → Resources → Memory and restart Docker.
Both linux/amd64 and linux/arm64 are published, so Apple Silicon runs
natively with no emulation.
Run it
The command above. The first boot initialises the database, applies the
migrations, and creates a workspace called default — a few minutes on a
laptop. Follow it with docker logs -f classifyre.
Connect a source
Point it at something you already run — a database, an S3 bucket, a Confluence space, a Git repository, or a folder you mounted. Credentials are encrypted at rest. See configuring sources.
Switch on detectors
Enable the pre-built packs you care about. They work on the first scan with no model setup.
Run a scan, open a case
Findings land ranked by importance. Group them into inquiries and cases, or add an AI provider and let the autopilot work them between scans.
Scanning a folder on your machine
Bind-mount it, then point a source at the path inside the container:
docker run -d --name classifyre \
-p 3000:3000 --shm-size=1g \
-v classifyre-pgdata:/var/lib/postgresql/data \
-v classifyre-data:/var/lib/classifyre \
-v classifyre-uv-cache:/cache/uv \
-v "$HOME/Documents/case-files:/data/case-files:ro" \
classifyre/all-in-one:latestIn the UI: Sources → New → Mounted Folder, path /data/case-files. See
Mounted Folder.
Mount read-only (:ro) unless you have a reason not to. Classifyre only ever
reads from a folder source, and :ro makes that the filesystem’s guarantee
rather than the application’s.
Where the data lives
Three volumes, and you want all three:
| Volume | Holds | If you drop it |
|---|---|---|
/var/lib/postgresql/data | the database | everything is lost |
/var/lib/classifyre | scan logs, the credential encryption key, the model cache | stored credentials become unreadable |
/cache/uv | optional Python dependencies installed on first use | they are downloaded again after every upgrade |
Configuration
Everything is an environment variable, and everything has a working default.
Pass them with -e NAME=value, or with --env-file.
Core
| Variable | Default | Purpose |
|---|---|---|
PORT_HTTP | 3000 | Port inside the container. Usually easier to remap with -p 8080:3000. |
CLASSIFYRE_BOOTSTRAP_NAMESPACE | default | Slug of the workspace created on first boot. Set to "" to create your own. |
CLASSIFYRE_MASKED_CONFIG_KEY | generated | Encrypts stored source credentials and MCP tokens. |
DEMO_MODE | false | Read-only mode; blocks every write. |
TELEMETRY_DISABLED / DO_NOT_TRACK | unset | Set either to 1 to disable telemetry. |
CORS_ORIGIN | same-origin | Only needed if the UI is served from another host. |
Database
Leave unset to use the PostgreSQL inside the image.
| Variable | Default | Purpose |
|---|---|---|
DATABASE_URL | bundled | Set it and the bundled server never starts. |
POSTGRES_DB | classifyre | Database name (bundled server only). |
PGDATA | /var/lib/postgresql/data | Data directory (bundled server only). |
CLASSIFYRE_AUTO_MIGRATE | true | false skips migrations at startup. |
An external server must have pgvector available, and the user must be able
to CREATE SCHEMA and CREATE EXTENSION — each workspace gets its own schema,
created on demand. See PostgreSQL.
-e DATABASE_URL="postgresql://user:pass@db.internal:5432/classifyre?sslmode=require"Object storage
Scan logs are written to disk by default. Point them at S3-compatible storage to keep them when the container is disposable. See S3 object storage.
| Variable | Default | Purpose |
|---|---|---|
S3_BUCKET | unset | Setting this switches scan logs from disk to S3. |
S3_ENDPOINT | AWS | Required for MinIO, R2, Backblaze, and anything non-AWS. |
S3_REGION | us-east-1 | |
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY | unset | Omit both to use the ambient credential chain. |
S3_FORCE_PATH_STYLE | true | Required by MinIO and most self-hosted providers. |
S3_LOG_PREFIX | runner-logs/ | Key prefix. |
Embeddings
| Variable | Default | Purpose |
|---|---|---|
EMBEDDING_PROVIDER | transformers-js | openai-compatible to use a remote service. |
EMBEDDING_BASE_URL / EMBEDDING_API_KEY | unset | The remote endpoint, when using openai-compatible. |
EMBEDDING_MODEL | Xenova/all-MiniLM-L6-v2 | Changing it re-embeds everything. |
EMBEDDING_ALLOW_REMOTE_MODELS | false | true allows downloading a different local model. |
See embeddings for what changing the model costs.
AI providers for LLM detectors and the autopilot are not environment variables. They are per-workspace settings configured in the UI under Settings → AI providers, with their keys encrypted at rest.
Resources
The container measures the memory and CPU it was given and sizes itself at startup. Override only with reason.
| Variable | Purpose |
|---|---|
CLASSIFYRE_NODE_HEAP_MB | Node heap ceiling. Bigger is usually worse — a larger ceiling means more garbage accrues before a major GC. |
PG_SHARED_BUFFERS, PG_WORK_MEM, PG_EFFECTIVE_CACHE_SIZE | PostgreSQL memory. |
MAX_CONCURRENT_RUNNERS | Scans running at once. |
CLASSIFYRE_MAX_POOL_WORKERS | Detector worker processes per scan. |
CLASSIFYRE_MIN_MEMORY_MB | The 4 GB startup floor. |
The startup log prints what it chose:
[classifyre] memory=8192MB cpus=4 shm=1024MB
[classifyre] node heap=2048MB rss guard=4928MB
[classifyre] postgres shared_buffers=512MB work_mem=16MB effective_cache_size=4096MB
[classifyre] scans: 1 concurrent, 4 worker(s) eachUpgrades
docker pull classifyre/all-in-one:latest
docker rm -f classifyre
# …then the same `docker run`, with the same volumesData lives in the volumes, so it carries over, and migrations run automatically on the first boot of the new version. Pin a version tag to control when that happens. Downgrades are not supported — migrations only go forwards. See upgrade & versioning.
Backup
Two things, and you need both:
docker exec classifyre pg_dump -U postgres classifyre > classifyre.sql
docker exec classifyre cat /var/lib/classifyre/masked-config.keyWithout the key, a restored dump still holds every stored source credential —
encrypted, and permanently unreadable. Keep it where you would keep a
password, and restore it with -e CLASSIFYRE_MASKED_CONFIG_KEY=….
Troubleshooting
Won’t start
The container exits saying there is not enough memory: Docker is capping it
below 4 GB. Raise Settings → Resources → Memory in Docker Desktop and
restart Docker, or check for a --memory flag.
Moving to Kubernetes
Nothing here is a dead end. Export a workspace from this container and import it into a cluster install — sources, findings, cases and lineage all move. The Helm chart runs the same images this one is composed from.