Configuration
qnop is configured in two places, and knowing which is which saves a lot of time:
- Environment variables — infrastructure. Where the database is, where the bucket is, which secrets to use. Changing them needs a restart.
- Administration → Settings — everything an operator tunes while the system runs: SMTP, upload limits, self-registration, retention. These are not environment variables.
Every environment variable is prefixed QNOP_.
The three secrets
Section titled “The three secrets”These have no usable default. Each ships as CHANGE_ME, which is rejected at
startup — the server refuses to boot rather than run insecurely.
| Variable | Purpose | Requirement |
|---|---|---|
QNOP_AUTH_JWT_SECRET | signs access tokens | ≥ 32 characters |
QNOP_AUTH_ENCRYPTION_KEY | encrypts secrets held at rest (e.g. OIDC client secrets) | ≥ 32 characters |
QNOP_AUTH_ENCRYPTION_SALT | key-derivation salt | hex-encoded, ≥ 16 characters |
QNOP_AUTH_JWT_SECRET="$(openssl rand -base64 48)"QNOP_AUTH_ENCRYPTION_KEY="$(openssl rand -base64 48)"QNOP_AUTH_ENCRYPTION_SALT="$(openssl rand -hex 32)"Note the salt uses -hex, not -base64.
Database
Section titled “Database”| Variable | Default | Purpose |
|---|---|---|
QNOP_DB_HOST | localhost | Postgres host |
QNOP_DB_PORT | 5432 | Postgres port |
QNOP_DB_NAME | qnop | database name |
QNOP_DB_USERNAME | qnop | user |
QNOP_DB_PASSWORD | qnop | password |
The defaults are development values — set all of them in production. The
standard SPRING_DATASOURCE_* variables override the URL assembled from these
if you need full control.
Object storage
Section titled “Object storage”| Variable | Default | Purpose |
|---|---|---|
QNOP_S3_ENDPOINT | (empty) | endpoint URL — leave empty for real AWS S3 |
QNOP_S3_BUCKET | qnop-documents | bucket name |
QNOP_S3_ACCESS_KEY | qnop | access key |
QNOP_S3_SECRET_KEY | qnop_secret | secret key |
QNOP_S3_REGION | us-east-1 | region, or the placeholder your vendor requires |
QNOP_S3_PATH_STYLE_ACCESS | true | true for MinIO; set false for AWS S3 |
QNOP_S3_AUTO_CREATE_BUCKET | false | create the bucket on first start |
QNOP_S3_REAPER_GRACE_PERIOD | PT1H | grace before a staged, unreferenced object is swept |
QNOP_S3_REAPER_CRON | 0 30 3 * * * | when that sweep runs |
The defaults are tuned for the bundled MinIO. Pointing at AWS S3 means clearing
the endpoint and flipping QNOP_S3_PATH_STYLE_ACCESS to false.
HTTP and proxying
Section titled “HTTP and proxying”| Variable | Default | Purpose |
|---|---|---|
QNOP_HTTP_FORWARD_HEADERS_STRATEGY | none | set framework behind a trusted reverse proxy |
QNOP_CORS_ALLOWED_ORIGINS | http://localhost:5173 | comma-separated exact origins; not needed for the embedded UI |
QNOP_HTTP_SHUTDOWN_GRACE | 30s | graceful-shutdown drain |
QNOP_HTTP_CONNECTION_TIMEOUT | 20s | connection timeout |
QNOP_HTTP_KEEP_ALIVE_TIMEOUT | 20s | keep-alive reap |
QNOP_HTTP_REQUEST_TIMEOUT | 30s | async dispatch cap |
Outbound timeouts — used for OIDC discovery, JWKS fetches and SMTP — are
QNOP_HTTP_CLIENT_OUTBOUND_CONNECT_TIMEOUT (5s),
QNOP_HTTP_CLIENT_OUTBOUND_READ_TIMEOUT (15s), and the
QNOP_HTTP_CLIENT_SMTP_* trio (10s connect, 30s read, 30s write).
Uploads
Section titled “Uploads”| Variable | Default | Purpose |
|---|---|---|
QNOP_UPLOAD_MULTIPART_LIMIT_MB | 55 | servlet-level multipart ceiling |
Authentication
Section titled “Authentication”| Variable | Default | Purpose |
|---|---|---|
QNOP_AUTH_OIDC_FRONTEND_BASE_URL | (empty) | where to return after OIDC login; empty = same origin |
QNOP_AUTH_OIDC_ALLOW_PRIVATE_DISCOVERY_URIS | false | SSRF guard — see below |
QNOP_AUTH_RATE_LIMIT_TRUSTED_PROXY_CIDRS | (empty) | which hops may set the client IP |
OIDC providers themselves are not configured by environment variable — they are managed at runtime under Administration → OIDC providers. See Authentication.
Document conversion
Section titled “Document conversion”DOCX uploads are converted to PDF by a headless LibreOffice, which the official image ships. The same converter renders annotation exports.
| Variable | Default | Purpose |
|---|---|---|
QNOP_OFFICE_BINARY | soffice | the LibreOffice executable |
QNOP_OFFICE_TIMEOUT | 60s | how long one conversion may run |
QNOP_OFFICE_MAX_CONCURRENT | 2 | conversions running at once |
QNOP_OFFICE_MAX_WAIT | 30s | how long a conversion waits for a free slot |
A conversion that finds every slot busy for longer than QNOP_OFFICE_MAX_WAIT
is refused: an export answers 503 with Retry-After, and an ingest job simply
retries later. Without LibreOffice — a custom image, say — PDF keeps working and
DOCX is not offered.
Usage tracking
Section titled “Usage tracking”The tracking provider itself is configured in the admin area. Two decisions sit in the environment because they must not be reachable from the web UI:
| Variable | Default | Purpose |
|---|---|---|
QNOP_TRACKING_ALLOW_PRIVATE_HOST | false | let the tracking proxy reach a private or loopback address |
QNOP_TRACKING_RATE_LIMIT_MAX_ATTEMPTS | 120 | request budget of the tracking proxy |
QNOP_TRACKING_RATE_LIMIT_WINDOW_SECONDS | 60 | the window those attempts are counted in |
Enable QNOP_TRACKING_ALLOW_PRIVATE_HOST only for a self-hosted analytics
server on your internal network — the same switch would let the server call
cloud metadata addresses.
Instance limits and capabilities
Section titled “Instance limits and capabilities”These exist for operators who run qnop for someone else — a hosted plan, in
practice. An administrator inside the instance can neither see nor change them,
which is the point. The defaults change nothing: every limit is 0
(unlimited) and every capability is on, so a self-hosted deployment needs none
of this.
| Variable | Bounds |
|---|---|
QNOP_LIMITS_MAX_USERS | user accounts |
QNOP_LIMITS_MAX_TEAMS | teams |
QNOP_LIMITS_MAX_TEAM_MEMBERS | members per team |
QNOP_LIMITS_MAX_ACTIVE_REVIEWS | reviews in draft, in review or changes requested |
A limit stops the next record, never an existing one: lowering it below current usage leaves everything in place.
The capability switches, all true by default, are
QNOP_FEATURES_OIDC, QNOP_FEATURES_ANNOTATION_EXPORT,
QNOP_FEATURES_CUSTOM_BRANDING, QNOP_FEATURES_SCHEDULER_MANUAL_RUN,
QNOP_FEATURES_SCHEDULER_JOB_SETTINGS, QNOP_FEATURES_SMTP_CONFIGURATION,
QNOP_FEATURES_EMAIL_TEMPLATES, QNOP_FEATURES_USAGE_TRACKING,
QNOP_FEATURES_UPLOAD_CONSTRAINTS, QNOP_FEATURES_SELF_REGISTRATION and
QNOP_FEATURES_DEPLOYMENT_CONFIGURATION. Switching one off closes the door
rather than hiding the handle — with QNOP_FEATURES_OIDC=false, for example,
the OAuth2 endpoints are not mounted at all. Most of the others make a group of
admin settings read-only or unavailable; the application.yml in the
repository documents each one.
Bootstrap
Section titled “Bootstrap”| Variable | Default | Purpose |
|---|---|---|
QNOP_ADMIN_PASSWORD | (unset) | fixed initial admin password |
Unset, qnop generates a one-time password on first start and forces a change at first login. Set, it uses yours and skips the forced change.
What is not an environment variable
Section titled “What is not an environment variable”These live in Administration → Settings and change without a restart. The list matters because operators regularly go looking for them in the environment:
- General — application name, base URL, default language and timezone.
- Uploads —
document_max_file_size_mb(default 25),attachment_max_file_size_mb(default 10). - SMTP — enabled, host, port, credentials, encryption, from-address and from-name. Mail is configured here, not in the environment.
- Authentication — self-registration and its default role, password reset and its token lifetime.
- Notifications — whether review e-mails are sent, and how long in-app
notifications are kept (
notifications.retain_days). - Usage tracking — the analytics provider, host, site id, consent and opt-outs, including whether the client IP is forwarded to it.
Each person additionally chooses, in their own profile, their interface language and how review activity reaches them by e-mail: as it happens, once a day as a morning digest (the default), or not at all. Mentions arrive straight away either way. Mail is written in the recipient’s language, falling back to the application’s default language and then to English; the shipped templates cover English, German, Spanish, French and Brazilian Portuguese, and each can be edited per language under Administration → Email.
- Review — free reattach, finalize-with-open-annotations, and the archive and purge retention windows.
Advanced knobs
Section titled “Advanced knobs”A number of further variables exist and bind by naming convention, but are not
part of the documented contract and can change without notice: token lifetimes
(QNOP_AUTH_ACCESS_TOKEN_TTL, QNOP_AUTH_REFRESH_TOKEN_TTL), the re-anchoring
similarity thresholds (QNOP_REANCHORING_*), the per-endpoint rate-limit
budgets (QNOP_AUTH_RATE_LIMIT_*), and the maintenance cron expressions
(QNOP_S3_ORPHAN_REAPER_CRON, QNOP_REVIEW_ARCHIVE_CRON,
QNOP_REVIEW_PURGE_CRON). Reach for them only with a specific reason.