Skip to content

Environment Variables

Most runtime variables use the OXICLOUD_ prefix. A few build-time or allocator variables do not.

Server

VariableDefaultDescription
OXICLOUD_STORAGE_PATH./storageRoot storage directory
OXICLOUD_STATIC_PATH./staticStatic files directory
OXICLOUD_TEMP_DIRstd::env::temp_dir() ($TMPDIR)Directory for tier-1 temporary data — pure scratch, safe to lose at reboot. Backend services stream blobs here when extractors need a &Path (id3, mp3_duration, ffprobe, nom-exif video). Files are auto-removed after use. On Linux /tmp is often tmpfs (RAM-backed); point at a disk-backed dir under RAM-constrained deployments.
OXICLOUD_SERVER_PORT8086Server port
OXICLOUD_SERVER_HOST127.0.0.1Server bind address (IPv4 or IPv6 allowed)
OXICLOUD_BASE_URL(auto)Public base URL for share links; defaults to http://{host}:{port}
OXICLOUD_MAX_UPLOAD_SIZE10737418240Whole-file size ceiling, in bytes (10 GB on 64-bit, 1 GB on 32-bit). Applies to BOTH direct PUTs (per-request body) and chunked uploads (declared total_size, checked upfront at session creation).
OXICLOUD_DIRECT_PUT_MAX_BYTES1073741824Per-request cap for non-chunked PUT bodies, in bytes (1 GiB). Set below OXICLOUD_MAX_UPLOAD_SIZE so larger files are pushed onto the chunked protocol (resumable on failure). See Storage Fine Tuning.
OXICLOUD_CHUNK_MAX_BYTES104857600Maximum size of a single chunked-upload PUT in bytes (100 MB). Per-chunk cap, independent of OXICLOUD_MAX_UPLOAD_SIZE (whole-file cap). See Storage Fine Tuning.
OXICLOUD_CHUNK_DIR{STORAGE_PATH}/.uploadsRoot directory for chunked-upload sessions (REST + NextCloud). Direct (non-chunked) uploads stream straight into the blob store and need no spool directory. Placement guidance: see Storage Fine Tuning.
OXICLOUD_REUSE_PORTfalseEnable SO_REUSEPORT so multiple processes can share the same port. Disabled by default — a second accidental instance will fail with "address already in use". Enable only for deliberate multi-worker setups (process supervisor, rolling restart). Not supported on Windows.
OXICLOUD_METRICS_LISTEN(unset)Prometheus /metrics listener address (e.g. 127.0.0.1:9090, IPv6 allowed as [::1]:9090). Unset = disabled: no /metrics endpoint is bound and no metrics recorder is installed (zero runtime cost). When set, a separate HTTP listener on this address serves the text-format scrape. Deliberately NOT merged into the main API — no auth, CSRF, or DPoP layer in front. Bind to loopback or a private interface unless you intend to expose metrics publicly. Starter counters: oxicloud_dpop_verify_failed_total{reason}, oxicloud_dpop_proof_missing_total, oxicloud_dpop_header_missing_on_bound_session_total, oxicloud_dpop_replay_detected_total, oxicloud_dpop_nonce_challenges_issued_total.
OXICLOUD_STARTUP_JOBSthumb_derived_import?repair=true,thumb_attached_import?repair=true,transcode_import?repair=trueBackground jobs dispatched once at boot, comma-separated, each name or name?flag=true using the same syntax as POST /api/admin/jobs/{name}/trigger. Flags: force, deep, repair, storage. The default migrates thumbnails out of the legacy .thumbnails/ directory and deletes the originals, so the migration completes without anyone triggering it from the admin panel; each sidecar is read back through the normal stack before it is unlinked, and every deletion is audited. An explicit value replaces the default; set it empty (OXICLOUD_STARTUP_JOBS=) to disable startup jobs, or to thumb_derived_import,thumb_attached_import to import without deleting. Non-blocking — readiness never waits on a job; entries run sequentially in the background. Fail-fast — an unknown job name or flag panics at boot, because a silently-dropped entry means a migration that never runs. A run interrupted by a restart resumes from its cursor on the next boot, so a long migration finishes across restarts. Safe to leave at the default: the jobs are idempotent, and once drained a run does nothing. See Thumbnail Migration for the upgrade runbook.

Database

VariableDefaultDescription
OXICLOUD_DB_CONNECTION_STRINGpostgres://postgres:postgres@localhost:5432/oxicloudPostgreSQL connection string
OXICLOUD_DB_MAX_CONNECTIONS20Max pool connections
OXICLOUD_DB_MIN_CONNECTIONS5Min pool connections
OXICLOUD_DB_MAINTENANCE_MAX_CONNECTIONS5Max connections in the isolated maintenance pool
OXICLOUD_DB_MAINTENANCE_MIN_CONNECTIONS1Min connections in the isolated maintenance pool

Build-Time SQLx

VariableDefaultDescription
DATABASE_URLBuild-time database URL for SQLx compile-time checks

Authentication

VariableDefaultDescription
OXICLOUD_JWT_SECRET(auto-generated)JWT signing secret; auto-persisted to <STORAGE_PATH>/.jwt_secret if unset
OXICLOUD_ACCESS_TOKEN_EXPIRY_SECS3600Access token lifetime (1 hour)
OXICLOUD_REFRESH_TOKEN_EXPIRY_SECS604800Refresh token lifetime (7 days); active sessions auto-renew on use
OXICLOUD_HASH_MEMORY_COST65536Argon2id memory cost in KiB (64 MiB). Server-side — used by the legacy password path (POST /api/auth/login) and the app-password Basic-Auth verifier. Distinct from OXICLOUD_AUTH_OPAQUE_KSF_* (client-side).
OXICLOUD_HASH_TIME_COST3Argon2id iteration count for the server-side legacy path.
OXICLOUD_HASH_PARALLELISM2Argon2id parallelism lanes for the server-side legacy path.
OXICLOUD_DISABLE_REGISTRATIONfalseDisable registration of new user accounts
OXICLOUD_REGISTRATION_ALLOWED_EMAIL_DOMAINSComma-separated allowlist of email domains accepted on POST /api/auth/register (case-insensitive, exact match on the post-@ part). Empty = any domain is allowed. Distinct from OXICLOUD_EXTERNAL_EMAIL_DOMAINS: this one gates SELF-registration (public sign-up), the external list gates INVITATIONS (grants + magic-link to third parties). An operator can lock sign-up to their company domain while leaving invitations open. Subdomains must be listed explicitly. Rejected registrations return 403 RegistrationDomainNotAllowed and emit an audit line. Example: mycompany.com,mycompany-eu.com.
OXICLOUD_AUTH_METHODSpassword,magic_linkComma-separated allowlist of auth methods (password, magic_link, oidc). Fail-fast: unknown token → boot panic; empty allowlist → boot panic; oidc in list without OXICLOUD_OIDC_ENABLED=true → boot panic. Removing password disables POST /api/auth/login (returns 403 PasswordLoginDisabled) and password-based register (returns 403 PasswordRegistrationDisabled). Removing magic_link disables POST /api/auth/magic-link/send (returns 403 MagicLinkLoginDisabled) and the redemption path for login-purpose tokens. Setting OXICLOUD_AUTH_METHODS=oidc is the cleanest "SSO-only" posture. Loose semantic (deprecation warning): if this list is explicitly set WITHOUT oidc but OXICLOUD_OIDC_ENABLED=true, OIDC is served regardless — a boot warning is emitted and this will become a fail-fast panic in the next major release. Startup gate: if magic_link is the only working method (no password, no oidc) AND no SMTP transport is configured (OXICLOUD_SMTP_HOST empty), the server refuses to start. OIDC master rule: when OIDC is enabled, magic-link login is hard-disabled regardless of this list (would otherwise bypass IdP-enforced MFA / step-up). Legacy alias (DEPRECATED): OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN=true still removes password from the list but emits a boot warning; removal in next major release.
OXICLOUD_AUTH_POLICIESComma-separated additive policy switches. Each token grants an exception or restriction to the default auth behaviour; empty (unset) = pure defaults. Recognised tokens: permit_magic_link_for_password_users (allow magic-link login for accounts that also have a password — off by default because magic-link would weaken the password to mailbox-strength; OIDC-linked users are still refused regardless); auto_redirect_if_standalone_oidc (when OIDC is the only working login method, auto-redirect the login page to the IdP instead of showing a click-to-continue button — off by default to avoid redirect loops on IdP failure and preserve logout UX).
OXICLOUD_REQUIRE_VERIFIED_EMAILfalseWhen true, POST /api/auth/login returns 403 EmailNotVerified for any account whose email_verified_at is NULL. Users can prove control by requesting a magic-link (whose redemption stamps email_verified_at), so this composes with magic_link in OXICLOUD_AUTH_METHODS to give users a self-service verification path. Admin-created (POST /api/admin/users) and setup-admin (POST /api/setup) users are auto-verified. OIDC-JIT users are also stamped verified at creation.

OPAQUE aPAKE (zero-knowledge password login)

OPAQUE (RFC 9807) is a zero-knowledge password-authenticated key exchange: the passphrase never leaves the client, not on registration and not on login. It's shipped in stages (see docs/plan/opaque.md); this build carries the substrate only — endpoints are inert until OXICLOUD_AUTH_OPAQUE_MODE is set. See docs/config/authentication.md for the phase rollout, the migration plan, and admin-facing guidance.

VariableDefaultDescription
OXICLOUD_AUTH_OPAQUE_MODEoffRuntime mode. off = endpoints 404 (default). migrate = endpoints live, legacy POST /api/auth/login still accepted. opaque_only = endpoints live, legacy refused for users with an envelope. Effective-mode cross-check: when password is not in OXICLOUD_AUTH_METHODS, the mode is auto-downgraded to off with an audit-channel INFO line (OPAQUE only replaces the password path — nothing to shadow in an OIDC-only or magic-link-only deployment). So OIDC / magic-link-only operators can safely ignore every OXICLOUD_AUTH_OPAQUE_* variable.
OXICLOUD_AUTH_OPAQUE_SERVER_SETUPBase64-encoded ServerSetup blob. Required when OXICLOUD_AUTH_OPAQUE_MODE != off AND password is enabled — the server refuses to start with a helpful error otherwise. Generate once with the oxicloud opaque setup subcommand and persist the value like your JWT secret. Never rotate — rotating invalidates every user's envelope (they'd all need to reset their passphrase).
OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB47104Client-side Argon2id memory cost in KiB (46 MiB — matches OWASP interactive-auth recommendation). Runs on the user's device during OPAQUE login/registration, TWICE per login. Distinct from OXICLOUD_HASH_MEMORY_COST (server-side legacy path). Bumping raises brute-force cost after a hypothetical envelope leak but also raises login latency and risks WASM heap OOM on low-memory devices — see authentication.md § OPAQUE — KSF parameters for the full rationale + per-device latency table.
OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS1Client-side Argon2id iteration count (OWASP interactive-auth recommendation).
OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM1Client-side Argon2id parallelism lanes (OWASP recommendation). Higher only helps on multi-core hardware and can hurt single-core / older mobile devices.

DPoP cryptographically binds a session cookie to a browser-held ECDSA keypair (P-256, non-extractable via SubtleCrypto). Every request carries a signed proof the middleware verifies against the session's binding. Closes the info-stealer replay threat: a cookie copied to another machine is useless without the private key. Non-browser clients (Nextcloud sync via app passwords, CLI via device-authorization) never bind and are exempted at the middleware regardless of mode. See docs/plan/dpop.md for the full rollout plan.

VariableDefaultDescription
OXICLOUD_DPOP_MODEoffEnforcement mode. off = middleware pass-through (default, ship-safe). opportunistic = verify when a proof is present, log dpop.header_missing_but_session_bound audit when absent on a bound session, but allow the request through (rollout mode — catches client bugs). required = bound sessions MUST present a valid proof or 401. Unbound sessions always exempt. Recommended rollout: offopportunistic for 2-4 weeks → required. For DPoP to be meaningful, cookies must be Secure (OXICLOUD_COOKIE_SECURE=true in production over HTTPS) and reverse-proxy X-Forwarded-Proto / X-Forwarded-Host must reach the app so the htu claim canonicalises correctly.

Rate Limiting & Account Lockout

VariableDefaultDescription
OXICLOUD_RATE_LIMIT_LOGIN_MAX10Max login attempts per IP per window
OXICLOUD_RATE_LIMIT_LOGIN_WINDOW_SECS60Login rate-limit window (seconds)
OXICLOUD_RATE_LIMIT_REGISTER_MAX5Max registration attempts per IP per window
OXICLOUD_RATE_LIMIT_REGISTER_WINDOW_SECS3600Registration rate-limit window (seconds)
OXICLOUD_RATE_LIMIT_REFRESH_MAX20Max token refresh attempts per IP per window
OXICLOUD_RATE_LIMIT_REFRESH_WINDOW_SECS60Refresh rate-limit window (seconds)
OXICLOUD_RATE_LIMIT_USER_PROFILE_MAX60Max user-profile lookups per caller per window
OXICLOUD_RATE_LIMIT_USER_PROFILE_WINDOW_SECS60User-profile lookup window (seconds)
OXICLOUD_RATE_LIMIT_DELTA_UPLOAD_MAX240Max delta-upload requests per caller per window
OXICLOUD_RATE_LIMIT_DELTA_UPLOAD_WINDOW_SECS60Delta-upload window (seconds)
OXICLOUD_LOCKOUT_MAX_FAILURES5Consecutive failed logins before account lockout
OXICLOUD_LOCKOUT_DURATION_SECS900Account lockout duration (15 minutes)

Login, register and refresh are keyed on the client IP and guard the unauthenticated front door. User-profile and delta-upload are keyed on the caller id and guard an authenticated user against exhausting a shared resource.

That distinction decides which knob to reach for. When several actors share one identity — a CI suite, an integration bot, a kiosk account — they share one caller bucket, and a ceiling sized for a single human is easily exceeded while the IP-keyed limits sit untouched. Raising the login/register/refresh limits does nothing in that case.

Exceeding the user-profile limit returns 429, which in a browser usually shows up as owner names failing to resolve in file listings rather than as a visible error.

Feature Flags

VariableDefaultDescription
OXICLOUD_ENABLE_AUTHtrueEnable authentication
OXICLOUD_ENABLE_USER_STORAGE_QUOTASfalsePer-user storage quotas
OXICLOUD_ENABLE_FILE_SHARINGtrueFile/folder sharing
OXICLOUD_ENABLE_TRASHtrueTrash / recycle bin
OXICLOUD_ENABLE_SEARCHtrueFull-text and metadata search
OXICLOUD_ENABLE_MUSICtrueMusic playlists and audio metadata
OXICLOUD_ENABLE_VIDEO_THUMBNAILStrueServer-side single-frame thumbnail extraction from uploaded videos (one frame → WebP). Requires ffmpeg on PATH (override with OXICLOUD_FFMPEG_PATH). When true and ffmpeg is missing at boot, a WARN log is emitted and videos fall back to a placeholder icon. Set to false to skip the ffmpeg lookup entirely — useful on hosts where ffmpeg can't be installed, or when the client uploads video previews itself (some desktop/mobile clients generate thumbnails locally and POST them alongside the video).
OXICLOUD_FFMPEG_PATHffmpeg (on PATH)Absolute path to the ffmpeg binary. Ignored when OXICLOUD_ENABLE_VIDEO_THUMBNAILS=false. Useful for pinning a specific static build or when ffmpeg lives outside the default PATH.
OXICLOUD_EXPOSE_SYSTEM_USERStrueExpose other OxiCloud users as a read-only address book at GET /api/address-books
OXICLOUD_GRANT_CLEANUP_ENABLEDtrueBackground daemon that deletes expired rows from storage.role_grants. The authorization engine already filters expired grants out of every permission check at read time (expires_at IS NULL OR expires_at > NOW()), so leaving expired rows in place is a hygiene issue — not a security one. This daemon garbage-collects them daily. Set to false to keep every expired grant row forever (uncommon; a fresh install rarely wants this).
OXICLOUD_GRANT_CLEANUP_GRACE_DAYS15Days past a grant's expires_at before the row is eligible for deletion. The grace window preserves the audit / support answer to "what happened to my access?" for a couple of weeks past expiration. Values below 1 are legal but discouraged — the recommendation is ≥ 15 days. Values above the actual grant TTL used by clients waste index space; a few weeks is the sweet spot.
OXICLOUD_GRANT_CLEANUP_INTERVAL_HOURS24How often the grant-cleanup daemon fires. Clamped to a minimum of 1 hour. Adjusting this doesn't change what gets deleted — only how promptly. Daily is fine for any realistic grant volume.
OXICLOUD_WEBDAV_DRIVE_LISTING_PREFIX@driveNative WebDAV URL segment that renders the caller's drive list. Sanitized by trimming leading/trailing /. Three shapes: (1) default @drive/webdav/… addresses the caller's default personal drive (back-compat), /webdav/@drive/ returns the drive listing, /webdav/@drive/<uuid|name>/… targets a specific drive. (2) empty string ""/webdav/ IS the drive listing, /webdav/<uuid|name>/… targets a specific drive, no default-drive shortcut. (3) any other string (e.g. drives) — same shape as @drive with that segment substituted. Only drives the caller has Read on via role_grants resolve.

Storage Entries (multi-entry, recommended)

Declare one or more named storage backends. The one the app runs on is picked from the DB (admin_settings.storage.active_backend_name); the admin panel's storage tab flips the pointer, and cross-backend migration is a recoverable job that copies blobs between two entries with a read-only safety window. See Admin Settings — Storage & Migration for the operator flow and the multi-entry design doc for the full model.

VariableDefaultDescription
OXICLOUD_STORAGE_ENTRIESComma-separated allowlist of entry names. Names must match [a-z0-9_-]{1,32} and be unique. Order is preserved (the first entry is the fallback when the DB pointer is unset — fresh install).

Each declared name <N> then reads its own set of per-entry variables:

VariableDefaultDescription
OXICLOUD_STORAGE_<N>_BACKENDBackend type for entry <N>: local | s3 | azure (required per entry)
OXICLOUD_STORAGE_<N>_ROOT_DIROXICLOUD_STORAGE_PATHLocal-only: root directory for this entry's .blobs/. Falls back to the ambient OXICLOUD_STORAGE_PATH when unset.
OXICLOUD_STORAGE_<N>_S3_BUCKETS3-only: bucket name (required when backend=s3)
OXICLOUD_STORAGE_<N>_S3_REGIONus-east-1S3-only: AWS region
OXICLOUD_STORAGE_<N>_S3_ENDPOINT_URLS3-only: custom endpoint for non-AWS providers
OXICLOUD_STORAGE_<N>_S3_ACCESS_KEYS3-only: access key ID
OXICLOUD_STORAGE_<N>_S3_SECRET_KEYS3-only: secret access key
OXICLOUD_STORAGE_<N>_S3_FORCE_PATH_STYLEfalseS3-only: path-style URLs (required for MinIO, R2)
OXICLOUD_STORAGE_<N>_AZURE_ACCOUNT_NAMEAzure-only: storage account name
OXICLOUD_STORAGE_<N>_AZURE_ACCOUNT_KEYAzure-only: storage account key
OXICLOUD_STORAGE_<N>_AZURE_CONTAINERAzure-only: blob container name (required when backend=azure)
OXICLOUD_STORAGE_<N>_AZURE_SAS_TOKENAzure-only: SAS token (alternative to account key)
OXICLOUD_STORAGE_<N>_AZURE_ENDPOINT_URLAzure-only: custom endpoint (Azurite, private deployments)
OXICLOUD_STORAGE_<N>_ENCRYPTION_KEYComma-separated list of <cipher>:<base64 key> pairs (or bare <base64 key>, which defaults to aes-256-gcm). Presence implies encryption is enabled on this entry — no separate enable flag. The LAST pair wins on writes; every pair is a candidate for reads. Supported ciphers: aes-256-gcm and none (empty-key sentinel used at pair-list head/tail for encrypt/decrypt-in-place rotations). Bad base64, wrong length, duplicate keys, or multiple none pairs abort boot. See Storage key rotation for rotation recipes.

Fail-fast rules (boot aborts with actionable message):

  • A declared name whose required per-entry fields are missing (_BACKEND never set, S3 with no _S3_BUCKET, Azure with no _AZURE_CONTAINER).
  • Setting OXICLOUD_STORAGE_ENTRIES alongside any of the legacy flat vars below (OXICLOUD_STORAGE_BACKEND, OXICLOUD_S3_*, OXICLOUD_AZURE_*, OXICLOUD_STORAGE_ENCRYPTION_*). Pick one mode; the error lists every conflicting var to remove.
  • A DB pointer (admin_settings.storage.active_backend_name) that names an entry not in the current _ENTRIES. The error points at the repair flag oxicloud storage select <name> — verify + UPDATE DB + exit.

Example — two entries, local disk plus an S3 target for planned migration:

OXICLOUD_STORAGE_ENTRIES=local_main,s3_prod

OXICLOUD_STORAGE_local_main_BACKEND=local
OXICLOUD_STORAGE_local_main_ROOT_DIR=/srv/oxicloud

OXICLOUD_STORAGE_s3_prod_BACKEND=s3
OXICLOUD_STORAGE_s3_prod_S3_BUCKET=my-oxicloud-bucket
OXICLOUD_STORAGE_s3_prod_S3_REGION=us-east-1
OXICLOUD_STORAGE_s3_prod_S3_ACCESS_KEY=…
OXICLOUD_STORAGE_s3_prod_S3_SECRET_KEY=…
OXICLOUD_STORAGE_s3_prod_ENCRYPTION_KEY=aes-256-gcm:…  # openssl rand -base64 32

# Rotation window (two pairs, last wins on writes):
# OXICLOUD_STORAGE_s3_prod_ENCRYPTION_KEY=aes-256-gcm:<OLD>,aes-256-gcm:<NEW>

Storage Backend (DEPRECATED — legacy single-backend)

⚠️ Deprecated. Use Storage Entries above for new deployments. These flat variables still work when OXICLOUD_STORAGE_ENTRIES is unset — the parser then synthesises one entry named default from them, keeping pre-multi-entry .env files booting unchanged. Booting via this path emits a storage.legacy_flat_vars_deprecated warning so operators see it in logs. Removal target: not yet fixed; migrate at your convenience by moving each variable below into OXICLOUD_STORAGE_<NAME>_* form under an entry declared in OXICLOUD_STORAGE_ENTRIES. Setting any variable from this section alongside OXICLOUD_STORAGE_ENTRIES is a fail-fast boot error — pick one mode.

VariableDefaultDescription
OXICLOUD_STORAGE_BACKENDlocalBlob storage backend: local, s3, or azure

S3-Compatible (AWS S3, Backblaze B2, Cloudflare R2, MinIO)

Used when OXICLOUD_STORAGE_BACKEND=s3.

VariableDefaultDescription
OXICLOUD_S3_BUCKETS3 bucket name (required)
OXICLOUD_S3_REGIONus-east-1AWS region
OXICLOUD_S3_ACCESS_KEYAccess key ID
OXICLOUD_S3_SECRET_KEYSecret access key
OXICLOUD_S3_ENDPOINT_URLCustom endpoint for non-AWS providers (e.g. https://s3.example.com)
OXICLOUD_S3_FORCE_PATH_STYLEfalseForce path-style URLs (required for MinIO, R2)

Azure Blob Storage

Used when OXICLOUD_STORAGE_BACKEND=azure.

VariableDefaultDescription
OXICLOUD_AZURE_ACCOUNT_NAMEStorage account name (required)
OXICLOUD_AZURE_ACCOUNT_KEYStorage account key
OXICLOUD_AZURE_CONTAINERBlob container name (required)
OXICLOUD_AZURE_SAS_TOKENSAS token (alternative to account key)

Local Disk Cache for Remote Backends

A least-recently-used disk cache that can speed up repeated reads from S3 or Azure.

VariableDefaultDescription
OXICLOUD_STORAGE_CACHE_ENABLEDfalseEnable LRU disk cache
OXICLOUD_STORAGE_CACHE_MAX_SIZE53687091200Max cache size in bytes (50 GB)
OXICLOUD_STORAGE_CACHE_PATH{STORAGE_PATH}/.blob-cacheCache directory

Client-Side Encryption (DEPRECATED — per-entry key is the new home)

AES-256-GCM encryption applied to blobs before they are written to any backend.

⚠️ Deprecated. Prefer per-entry OXICLOUD_STORAGE_<NAME>_ENCRYPTION_KEY under an entry declared in OXICLOUD_STORAGE_ENTRIES — presence of the key implies encryption is enabled on that entry (no separate flag), and multi-entry enables cross-key rotation via migration to a new entry. The flat vars below still work in zero-entries mode and get folded into the synthesised default entry, alongside the same deprecation warning at boot.

VariableDefaultDescription
OXICLOUD_STORAGE_ENCRYPTION_ENABLEDfalseEnable at-rest blob encryption
OXICLOUD_STORAGE_ENCRYPTION_KEYBase64-encoded 32-byte encryption key; generate with openssl rand -base64 32

Retry Policy (Remote Backends)

Exponential backoff retries for transient errors on S3 and Azure.

VariableDefaultDescription
OXICLOUD_STORAGE_RETRY_ENABLEDtrueEnable retry with exponential backoff
OXICLOUD_STORAGE_RETRY_MAX_RETRIES3Maximum retry attempts
OXICLOUD_STORAGE_RETRY_INITIAL_BACKOFF_MS100Initial backoff in milliseconds
OXICLOUD_STORAGE_RETRY_MAX_BACKOFF_MS10000Maximum backoff cap in milliseconds
OXICLOUD_STORAGE_RETRY_BACKOFF_MULTIPLIER2.0Backoff multiplier per retry

OIDC / SSO

See the OIDC configuration guide for details.

VariableDefaultDescription
OXICLOUD_OIDC_ENABLEDfalseEnable OIDC
OXICLOUD_OIDC_ISSUER_URLOIDC issuer URL
OXICLOUD_OIDC_CLIENT_IDClient ID
OXICLOUD_OIDC_CLIENT_SECRETClient secret
OXICLOUD_OIDC_REDIRECT_URIhttp://localhost:8086/api/auth/oidc/callbackCallback URL (must match IdP config)
OXICLOUD_OIDC_SCOPESopenid profile emailRequested scopes
OXICLOUD_OIDC_FRONTEND_URLhttp://localhost:8086Frontend URL to redirect to after login
OXICLOUD_OIDC_AUTO_PROVISIONtrueAuto-create users on first SSO login (JIT provisioning)
OXICLOUD_OIDC_AUTO_LINK_EMAIL_MATCHtrueWhen subject-lookup misses on an OIDC login BUT the IdP-returned email (with email_verified=true) matches an existing local user (after +alias normalization), auto-link the OIDC identity to that user instead of refusing. Refuses on ambiguity (>1 local user normalises to same email) or if the matched user is already linked to a different identity. Safe under single-IdP trust model (admin chose the IdP); unsafe for future multi-IdP federation. Set false for postures requiring explicit consent for every link. See OIDC account linking plan.
OXICLOUD_OIDC_ADMIN_GROUPSComma-separated OIDC groups that grant admin role
OXICLOUD_OIDC_DISABLE_PASSWORD_LOGINfalseDEPRECATED — emits boot warning; slated for removal in next major release. Use OXICLOUD_AUTH_METHODS=oidc (and optionally OXICLOUD_AUTH_POLICIES=auto_redirect_if_standalone_oidc for server-side /login redirect) instead. Still removes password from the effective allowlist when set to true — kept working so upgrading deployments don't break.
OXICLOUD_OIDC_PROVIDER_NAMESSODisplay name for the provider shown in UI

WOPI (Office Editing)

See the WOPI configuration guide for details.

VariableDefaultDescription
OXICLOUD_WOPI_ENABLEDfalseEnable WOPI
OXICLOUD_WOPI_DISCOVERY_URLCollabora/OnlyOffice discovery URL
OXICLOUD_WOPI_BASE_URLOXICLOUD_BASE_URLURL the editor uses to call OxiCloud's /wopi/* endpoints
OXICLOUD_WOPI_PUBLIC_BASE_URLOXICLOUD_WOPI_BASE_URLURL the browser uses to open OxiCloud's WOPI host page
OXICLOUD_WOPI_SECRET(JWT secret)WOPI token signing key
OXICLOUD_WOPI_TOKEN_TTL_SECS86400Token lifetime (24 hours)
OXICLOUD_WOPI_LOCK_TTL_SECS1800Lock expiration (30 minutes)

When Collabora or OnlyOffice runs on a different hostname, set OXICLOUD_WOPI_PUBLIC_BASE_URL to the public OxiCloud URL that the browser can reach. If the editor reaches OxiCloud through a different internal URL, also set OXICLOUD_WOPI_BASE_URL for those callbacks.

Nextcloud Compatibility

Enables the Nextcloud-compatible API layer (/remote.php/, /ocs/, /status.php, Login Flow v2) for clients that use the Nextcloud protocol.

VariableDefaultDescription
OXICLOUD_NEXTCLOUD_ENABLEDfalseEnable Nextcloud compatibility layer
OXICLOUD_NEXTCLOUD_INSTANCE_IDocncaInstance ID suffix used in oc:id formatting
OXICLOUD_NEXTCLOUD_VERSION28.0.4Emulated Nextcloud version reported to clients (format: major.minor.patch)

Outbound Email (SMTP)

Used by the magic-link invitation flow and the login-via-email flow. When OXICLOUD_SMTP_HOST is empty (the default), the feature is disabled and any endpoint that needs email returns 503.

VariableDefaultDescription
OXICLOUD_SMTP_HOSTSMTP server hostname or IP. Empty disables the feature.
OXICLOUD_SMTP_PORT587Submission port (587 STARTTLS, 465 implicit TLS, 25 plain)
OXICLOUD_SMTP_USERSASL username. Leave empty for anonymous relay.
OXICLOUD_SMTP_PASSSASL password
OXICLOUD_SMTP_FROMFrom: mailbox; bare address or RFC 5322 name-address (OxiCloud <noreply@example.com>)
OXICLOUD_SMTP_TLSstarttlsTransport encryption: starttls, tls, or none (emits startup WARN)

There is also OXICLOUD_SMTP_MOCK (false by default), this is for test purpose only, do not activate it

Reliability and retries

OxiCloud does not spool mail. Each send() is a single attempt: if the remote SMTP server is unreachable, slow, or temporarily refusing the message, the send fails and the error is logged — there is no in-process retry, queue, or dead-letter handling. This keeps the HTTP path fast and the binary small at the cost of durability guarantees during a relay outage.

For production deployments where you cannot afford to drop invitation mail during a brief relay outage, point OxiCloud at a local MTA configured as a smarthost (Postfix, OpenSMTPD, exim, or msmtp-mta/nullmailer for minimal setups). The local MTA owns the durable queue: it accepts the message from OxiCloud in milliseconds over the loopback, then retries with its own exponential backoff against your real upstream relay until the message is delivered or the queue lifetime expires.

Typical local-relay config:

env
OXICLOUD_SMTP_HOST=127.0.0.1
OXICLOUD_SMTP_PORT=25
OXICLOUD_SMTP_TLS=none           # loopback only — never over the network
OXICLOUD_SMTP_FROM=OxiCloud <noreply@example.com>
# OXICLOUD_SMTP_USER / _PASS unset — local MTA accepts loopback unauthenticated

Then configure the local MTA's smarthost / relayhost to your upstream provider (SendGrid, Amazon SES, your corporate relay, etc.). Verify durability by stopping the upstream relay, sending an invitation, restarting the relay, and confirming the mail eventually arrives.

If you point OXICLOUD_SMTP_HOST directly at a remote SMTP server, treat the absence of retries as a documented constraint: a brief network glitch during invitation flow is a lost invite, and the recipient will need to be re-invited.

Configures the invite-by-email and login-via-email flows. Both require SMTP to be configured above.

VariableDefaultDescription
OXICLOUD_MAGIC_LINK_TTL_HOURS24Lifetime of a freshly-minted magic-link token, in hours
OXICLOUD_ALLOW_EXTERNAL_USERStrueKill switch for the whole flow. false makes POST /api/grants reject subject.type = "email" for unknown addresses and POST /api/auth/magic-link/send return its uniform stub without issuing a token.
OXICLOUD_EXTERNAL_EMAIL_DOMAINSComma-separated allowlist of email domains accepted when minting a new external user (case-insensitive, exact match on the post-@ part). Empty = any domain is allowed, subject to OXICLOUD_ALLOW_EXTERNAL_USERS. Subdomains must be listed explicitly: partner.com does NOT match eng.partner.com. Example: partner-a.com,partner-b.io.
OXICLOUD_NOTIFY_INTERNAL_USERS_ON_SHAREtrueOperator-level kill switch for the plain-notification email arm — the "Alice shared 'Project Alpha' with you" mail that fires when the recipient is a password user or OIDC user (i.e. not magic-link eligible). false suppresses the arm entirely; internal users discover new shares only at next login. A coarser knob than the per-user auth.users.notify_on_share column; when this is false the user-level opt-in does not matter. External-user magic-link first-invitations are unaffected and always send.

Internationalization (server-rendered surfaces)

Server-rendered HTML pages (magic-link landing, error pages) and outbound transactional emails go through the backend i18n layer. The set of available locales is discovered at boot by listing static/locales/*.json — no rebuild needed to add a 17th locale.

VariableDefaultDescription
OXICLOUD_DEFAULT_LOCALEenFallback locale used when no stronger signal is available. Must match one of the locales under static/locales/; startup fails fast if you set it to a code with no corresponding JSON file.

The resolution priority differs by surface:

  • HTML pages (anonymous, e.g. magic-link landing)?lang=xx query override, then the browser's Accept-Language header (q-weighted, with primary-tag fallback so fr-FR resolves to fr when no fr-FR.json is shipped), then this default.
  • Emails to a known user — the user's preferred_locale column (set via OIDC locale claim at JIT or via the UI language switcher), then this default.
  • Emails to a brand-new external user being invited — the inviter's preferred_locale (inheritance at row-creation), then this default.

Today's shipped locales: ar, de, en, es, fa, fr, hi, it, ja, ko, nl, pl, pt, ru, zh, zh-TW. Missing translations on a non-English locale automatically fall back to English at the key level — adding a new locale with even a few translated keys works without manual gap-filling.

Trusted Proxy

VariableDefaultDescription
OXICLOUD_TRUST_PROXY_CIDRComma-separated list of trusted proxy CIDRs; enables X-Forwarded-For / X-Real-IP extraction for those source IPs
OXICLOUD_TRUST_PROXY_HEADERSDeprecated. Use OXICLOUD_TRUST_PROXY_CIDR instead

Example: OXICLOUD_TRUST_PROXY_CIDR=127.0.0.1/32,10.0.0.0/8,172.16.0.0/12

Allocator Tuning

These variables are read directly by mimalloc, not by OxiCloud's config parser.

VariableDefaultDescription
MIMALLOC_PURGE_DELAY0Delay in ms before freed memory is returned to the OS (0 = immediately, recommended for Docker)
MIMALLOC_ALLOW_LARGE_OS_PAGES0Enable 2 MiB huge pages (0 = off, recommended for Docker to avoid THP RSS inflation)

Internal Defaults (not configurable via env)

ParameterDefault
File cache TTL60 s
Directory cache TTL120 s
Max cache entries10 000
Large file threshold100 MB
Streaming chunk size1 MB
Max parallel chunks8
Trash retention30 days
Argon2id memory cost64 MiB
Argon2id time cost3 iterations
Nextcloud Login Flow v2 TTL600 s

Released under the MIT License.