Skip to content
Get started

Configuration reference

The engine is configured entirely through environment variables, and it validates them at boot: a malformed value fails loudly with the variable’s name rather than quietly falling back to a default. If it starts, the config it read is the config you meant.

Most of what follows is optional. Two variables are not. A third is not if a browser embeds the widget.

Terminal window
MONGO_CONNECTION_STRING=mongodb://user:password@host:27017/cap_import
IMPORT_LICENSE_PUBLIC_KEY=<from the Self-hosting page of your account>
ALLOWED_ORIGINS=https://app.example.com # required to embed the widget; skip for API-only

That is a working engine — in-memory blob storage, no decode sidecars, everything else at its default. Add storage and sidecars as you need them.

Variable Default What it does
MONGO_CONNECTION_STRING Required. Connection string for MongoDB — or for FerretDB, a verified experimental backend. The database name comes from the URI path. Missing this is a crash at boot, not a warning.
IMPORT_LICENSE_PUBLIC_KEY Required. The public key licenses are verified against, base64-encoded PEM. Never a secret. Missing this is 403/503 on every import.
PORT 8090 HTTP port for the API.
ALLOWED_ORIGINS Comma-separated browser origins allowed to call the API. The production image refuses all cross-origin browser calls when this is empty — including localhost. Required if you embed the widget; skip only for server-to-server callers.

Large files stream to object storage rather than living in memory. Compose defaults to memory so docker compose up works as a first look. The Helm chart defaults to s3 because a cluster pod that loses files on every restart is not a deploy — you must set the bucket (and endpoint / credentials, or IRSA) or helm test fails. Empty S3 credentials with backend=s3 is a misconfiguration, not a working default.

Variable Default What it does
UNP_BLOB_BACKEND memory memory for dev and smoke tests, s3 for production.
S3_ENDPOINT S3 or MinIO endpoint. Required when the backend is s3.
S3_BUCKET Bucket name. Required when the backend is s3.
S3_REGION us-east-1 Region.
S3_ACCESS_KEY_ID Access key id.
S3_SECRET_ACCESS_KEY Secret access key.
S3_FORCE_PATH_STYLE Set true for MinIO and most S3-compatible stores that need path-style.

memory keeps uploads in the process, so they vanish on restart and cap out at whatever RAM you gave the container. Use it to prove the wiring works, then switch.

Each sidecar handles one family of file formats. Leave a URL unset and the engine simply declines those files — it degrades rather than breaks.

Variable Default What it does
UNP_PDF_PROBE_URL PDF table extraction. Unset: PDFs are not accepted.
UNP_XLSX_DECODE_URL Spreadsheet decoding. Unset: XLSX files are not accepted.
UNP_OCR_URL Scans and photos. Unset: image files are not accepted.
UNP_EMBED_URL Semantic-matching support pass. Unset: the feature is off.

Running the all-in-one image? The three decode sidecars (PDF, spreadsheets, scans) are wired internally — you can ignore those three URLs. Semantic matching still needs UNP_EMBED_URL pointing at a reachable embed service; leave it unset and that pass stays off.

Variable Default What it does
IMPORT_LICENSE_REVOKED_IDS Comma-separated license ids to refuse, even if otherwise valid.
IMPORT_LICENSE_DEVELOPMENT_MODE false Accepts any non-empty key as a synthetic dev license. Never set in production.

Development mode exists so you can run the engine before you have a key. It is ignored the moment IMPORT_LICENSE_PUBLIC_KEY is set, and the preflight flags it — so it cannot quietly survive into a real deployment.

Variable Default What it does
ALLOWED_ORIGINS Comma-separated browser origins allowed to call the API. Unset in production refuses cross-origin calls. Also listed under The minimum — do not skip it if you embed the widget.
THROTTLE_IMPORT_LIMIT 2000 Global requests-per-minute ceiling. Per-tenant limits come from the license itself.
IMPORT_INGEST_JOB_CONCURRENCY 2 How many uploads parse at once. Large files peak at several hundred MB each.
IMPORT_INGEST_LEASE_SECONDS 600 How long a parsing job may run before it is considered stalled and re-queued.
IMPORT_DATASET_FETCH_HOST_ALLOWLIST Host suffixes allowed for reference-dataset fetches. Empty leaves only the built-in guard.
UNP_EVENT_SINK_BACKEND noop noop for self-hosting. The alternative is for our hosted deployment.

The concurrency cap is about memory, not throughput: raising it multiplies peak RAM per pod. It is only worth raising once you have measured your own file sizes and have the headroom to match.

Variable Default What it does
IMPORT_METRICS_PORT 0 Prometheus /metrics on a separate internal port. 0 disables it. Never expose it publicly.

Metrics deliberately live on their own listener so you can expose them to your monitoring stack without widening the public API surface.

Some things are configured elsewhere on purpose:

  • AI settings live in the engine’s database, not in env vars — provider, endpoint, model and the encrypted API key are set once at runtime. Configure nothing and the importer stays fully deterministic.
  • Tuning constants — matching thresholds, sidecar timeouts, webhook retry schedules and retention windows — are fixed in the release rather than operator-configurable. They change through upgrades, so behaviour stays reproducible between deployments running the same tag.
Terminal window
pnpm import:self-host-check # from a source checkout
Terminal window
docker run --rm --env-file .env ghcr.io/capitality-io/mildport-aio \
bun dist/cli/self-host-check.js # from the image

It reports the resolved licensing, Mongo, CORS, storage, sidecar and metering setup, and exits non-zero on anything blocking — no Mongo URI, no public key with development mode off, s3 selected without a bucket. Empty ALLOWED_ORIGINS in production is a warning (the widget will fail CORS; API-only deploys are valid). On Kubernetes, helm test mildport runs the same check in-cluster.

Next: Licensing · FerretDB (experimental) · Air-gapped deployment