Skip to main content

Configuration

Every knob OpenBucket exposes, in one place. The standalone app reads its config from the environment; the embedded module (OpenBucketModule.forRoot) takes the same settings as typed options. Both fail fast — a missing or malformed secret refuses to boot rather than starting insecure.

Grab the essentials and go:

# The five required standalone settings:
DATA_DIR=/data
JWT_SECRET=$(openssl rand -base64 48)
ADMIN_PASSWORD_HASH=$(npx @openbucket/nestjs hash 'your-admin-password') # no repo checkout needed
# …or, from a repo clone: $(node scripts/hash-password.mjs 'your-admin-password')
ROOT_ACCESS_KEY_ID=AKIAEXAMPLE000000000
ROOT_SECRET_ACCESS_KEY=$(openssl rand -base64 48)

Everything else has a sensible default. The full, commented template lives in .env.example.

Generate strong secrets

JWT_SECRET, ROOT_SECRET_ACCESS_KEY, KEY_ENCRYPTION_SECRET, WEBHOOK_SECRET, and a token-mode METRICS_TOKEN are all validated as strong secrets: at least 32 characters, at least 8 distinct characters, not a single repeated character, and not a known placeholder (changeme, secret, password, …). openssl rand -base64 48 clears the bar every time.

Standalone environment variables​

The standalone app validates process.env on boot and refuses to start if anything required is missing or malformed. Unknown variables are ignored.

Core (required)​

VariableRequiredDefaultNotes
DATA_DIR✅—Directory for the SQLite metadata DB, blob payloads, and the generated sse.key. No trailing slash.
JWT_SECRET✅—Strong secret (≥ 32 chars). Signs admin JWTs.
ADMIN_PASSWORD_HASH✅—argon2id hash — generate with npx @openbucket/nestjs hash '<password>' (no repo checkout needed), or node scripts/hash-password.mjs '<password>' from a repo clone. See the hash command.
ROOT_ACCESS_KEY_ID✅—16–32 uppercase alphanumerics (^[A-Z0-9]{16,32}$).
ROOT_SECRET_ACCESS_KEY✅—Strong secret (≥ 32 chars). The root SigV4 credential.

Runtime & admin auth​

VariableDefaultNotes
NODE_ENVproductionOne of development / test / production.
PORT9000HTTP listen port.
MOUNT_PATH— (root)Route prefix everything mounts under — S3 (<MOUNT_PATH>/<bucket>/<key>), admin API (<MOUNT_PATH>/api/admin), admin console (<MOUNT_PATH>/admin), health & metrics. Empty = root. Set it to run behind a reverse proxy at a subpath (e.g. /storage). Normalized to a leading slash, no trailing slash.
LOG_LEVELinfotrace / debug / info / warn / error / fatal.
ADMIN_USERNAMEadminBootstrap admin login.
JWT_ACCESS_TTL_SECONDS900Access-token lifetime (60–3600).
JWT_REFRESH_TTL_SECONDS604800Refresh-token lifetime (3600–2592000, i.e. 1 h–30 d).
Running at a subpath

MOUNT_PATH moves the whole OpenBucket surface under one prefix so it can sit behind a reverse proxy at, say, https://example.com/storage/… without a dedicated subdomain. The admin SPA's <base href> is rewritten to match, and the admin API stays guarded at <MOUNT_PATH>/api/admin/*. Point your S3 client's endpoint at https://example.com/storage (path-style). It is the standalone twin of the embedded module's OpenBucketModule.forRoot({ mountPath }). See Deployment → behind a reverse proxy.

S3 protocol​

VariableDefaultNotes
OPENBUCKET_REGIONus-east-1Region reported to clients. Match it in your SDK config.
OPENBUCKET_ENDPOINT—Optional DNS-safe hostname clients use to reach the store.
OPENBUCKET_SSE_KEYgeneratedbase64 of exactly 32 bytes for at-rest SSE-S3. Auto-generated to <DATA_DIR>/sse.key on first boot when unset.
KEY_ENCRYPTION_SECRETroot secretStrong secret. KEK material for scoped sub-key secrets at rest. Falls back to ROOT_SECRET_ACCESS_KEY when unset — so rotating the root secret without setting this invalidates existing sub-key secrets.

Limits & quotas​

VariableDefaultNotes
MAX_OBJECT_SIZE_MB5120Per-object size cap in MiB (5 GiB). Max 5242880.
MAX_MULTIPART_PARTS10000Max parts per multipart upload (also the ceiling).
MULTIPART_TTL_HOURS24Abandoned-multipart reaper TTL.
MAX_CONCURRENT_MULTIPART_UPLOADS1000Cap on in-flight multipart sessions. 0 = unlimited.
DATA_DIR_MIN_FREE_BYTES104857600Refuse writes below this free-space reserve (100 MiB).
STORAGE_QUOTA_BYTES0Aggregate stored-byte quota. 0 = disabled.
STORAGE_QUOTA_OBJECTS0Aggregate object-count quota. 0 = disabled.
S3_THROTTLE_LIMIT1000Per-IP S3 data-plane request budget per window. 0 disables.
S3_THROTTLE_TTL_MS60000S3 throttle window.

Image transforms​

On-the-fly, cached image derivatives (?w=&h=&fit=&format=&q=) on GET.

VariableDefaultNotes
IMAGE_TRANSFORM_ENABLEDtrueMaster switch. false makes every GET serve the plain object.
MAX_TRANSFORM_DIMENSION4096Max requested output width/height in px. Ceiling 16384.
MAX_TRANSFORM_INPUT_BYTES52428800Refuse to transform a source larger than this (50 MiB).
IMAGE_TRANSFORM_LIMIT_INPUT_PIXELS576000000Decoded-canvas ceiling (decompression-bomb guard, 24000×24000).
IMAGE_TRANSFORM_CONCURRENCY4Max concurrent sharp operations. Max 64.
DERIVATIVE_CACHE_MAX_BYTES5368709120Derivative-cache size ceiling (5 GiB). 0 = unbounded.

Analytics & audit​

VariableDefaultNotes
USAGE_ROLLUP_INTERVAL_MS900000Usage-rollup sample interval (floor 60000).
USAGE_RETENTION_DAYS90Prune usage samples older than this.
AUDIT_RETENTION_DAYS90Prune persisted audit rows older than this.
AUDIT_FLUSH_MS2000Audit buffer → SQLite flush interval (floor 250).
AUDIT_BUFFER_MAX10000Max buffered audit events before oldest is dropped (min 100).

Object-event webhooks​

Setting WEBHOOK_URL enables durable, HMAC-signed delivery of object events. The URL must be https unless the host is loopback; the secret is then required.

VariableRequiredDefaultNotes
WEBHOOK_URL—Target endpoint. Presence enables the delivery outbox.
WEBHOOK_SECRETwhen URL set—Strong secret. HMAC-SHA256 signing key.
WEBHOOK_EVENTSall threeComma-separated: object.created,object.deleted,multipart.completed.
WEBHOOK_MAX_ATTEMPTS8Attempts before dead-letter (1–50).
WEBHOOK_TIMEOUT_MS5000Per-request timeout (500–60000).
WEBHOOK_POLL_MS15000Delivery tick interval (1000–300000).

Async replication​

Mirror every object mutation to an external S3-compatible target. When OPENBUCKET_REPLICATION_ENABLED=true, the bucket and both credentials are required together — a partial config refuses to boot.

VariableRequired*DefaultNotes
OPENBUCKET_REPLICATION_ENABLEDfalseMaster switch. Off ⇒ zero cost, outbox stays empty.
OPENBUCKET_REPLICATION_ENDPOINT—S3-compatible endpoint (R2/B2/MinIO). Omit for real AWS S3. http:// warns at boot (plaintext).
OPENBUCKET_REPLICATION_REGIONus-east-1Target region.
OPENBUCKET_REPLICATION_BUCKET✅—Remote target bucket (must already exist).
OPENBUCKET_REPLICATION_ACCESS_KEY_ID✅—Target credential.
OPENBUCKET_REPLICATION_SECRET_ACCESS_KEY✅—Target credential (never logged).
OPENBUCKET_REPLICATION_FORCE_PATH_STYLEtruetrue for MinIO/S3-compat; false for AWS.
OPENBUCKET_REPLICATION_MAX_ATTEMPTS12Dead-letter cap (1–50).
OPENBUCKET_REPLICATION_DRAIN_INTERVAL_MS5000Drain tick interval (1000–300000).
OPENBUCKET_REPLICATION_BATCH_KEYS50Distinct keys drained per tick (1–1000).
OPENBUCKET_REPLICATION_LARGE_OBJECT_THRESHOLD_BYTES67108864Stream via multipart above this (64 MiB).

* Required only when OPENBUCKET_REPLICATION_ENABLED=true.

Cold-object tiering​

Offload cold objects to the replication target and rehydrate transparently on read. A no-op unless a replication target is also configured.

VariableDefaultNotes
OPENBUCKET_TIER_ENABLEDfalseMaster switch.
OPENBUCKET_TIER_INLINE_MAX_BYTES268435456Proxy read-through at/under this size; larger ⇒ presigned redirect (256 MiB).
OPENBUCKET_TIER_READTHROUGH_TIMEOUT_MS30000Latency bound on a proxied fetch before 503 SlowDown.
OPENBUCKET_TIER_MAX_CONCURRENT_REHYDRATE8Global concurrent-rehydration cap. 0 = unlimited.
OPENBUCKET_TIER_PRESIGN_TTL_SECONDS300TTL for presigned redirect URLs (30–3600).

Scheduled backups​

Write a .zip snapshot on a schedule with union retention. When OPENBUCKET_SCHEDULED_BACKUP_ENABLED=true, exactly one of INTERVAL_MINUTES / CRON must be set.

VariableDefaultNotes
OPENBUCKET_SCHEDULED_BACKUP_ENABLEDfalseMaster switch.
OPENBUCKET_SCHEDULED_BACKUP_SCOPEinstanceinstance = one snapshot; buckets = one per bucket.
OPENBUCKET_SCHEDULED_BACKUP_INTERVAL_MINUTES—Fixed interval (5–43200). Mutually exclusive with CRON.
OPENBUCKET_SCHEDULED_BACKUP_CRON—5-field cron. Mutually exclusive with INTERVAL_MINUTES.
OPENBUCKET_SCHEDULED_BACKUP_DIR<DATA_DIR>/backupsAbsolute snapshot directory.
OPENBUCKET_SCHEDULED_BACKUP_KEEP_LAST7Keep the newest N snapshots (hard floor, 1–1000).
OPENBUCKET_SCHEDULED_BACKUP_MAX_AGE_DAYS30Also keep anything younger than this (union, 1–3650).
OPENBUCKET_SCHEDULED_BACKUP_CHECK_INTERVAL_MS60000"Is a snapshot due?" wake tick (10000–3600000).
OPENBUCKET_SCHEDULED_BACKUP_PUSH_TO_REPLICATIONfalseAlso push each .zip to the replication target.

Integrity scrubbing​

A background scrubber re-hashes local blobs against their stored SHA-256 to detect bit-rot. Strictly rate-limited so it never starves request traffic.

VariableDefaultNotes
OPENBUCKET_INTEGRITY_SCRUB_ENABLEDfalseMaster switch. A fresh install does zero extra I/O.
OPENBUCKET_INTEGRITY_SCRUB_INTERVAL_MS60000Tick interval (floor 1000).
OPENBUCKET_INTEGRITY_SCRUB_MAX_OBJECTS_PER_TICK1000Hard per-tick object cap (min 1).
OPENBUCKET_INTEGRITY_SCRUB_MAX_BYTES_PER_TICK1073741824Per-tick byte budget (1 GiB).

Restore caps & shutdown​

VariableDefaultNotes
RESTORE_MAX_TOTAL_BYTES107374182400Total decompressed payload accepted from a restore archive (100 GiB).
RESTORE_MAX_ENTRY_BYTES5368709120Per-entry decompressed cap (5 GiB).
RESTORE_MAX_ENTRIES1000000Max payload entries in a restore archive.
RESTORE_MAX_MANIFEST_BYTES4194304Max buffered manifest.json bytes (4 MiB).
METRICS_MODEoffPrometheus scrape gate: off / public / token.
METRICS_TOKEN—Strong bearer token — required when METRICS_MODE=token.
OTEL_TRACING_ENABLEDfalseOpenTelemetry tracing (no-op unless @opentelemetry/api + an SDK are installed).
SHUTDOWN_DRAIN_MS30000In-flight drain budget on SIGTERM (1000–120000).

Module options (forRoot)​

When you embed OpenBucket, pass these to OpenBucketModule.forRoot(...) instead of environment variables. Only dataDir and rootCredentials are required.

Some standalone env features are env-only

The forRoot options are a deliberate subset of the standalone environment variables — a few features are configurable only via the environment and have no forRoot option counterpart:

  • KEY_ENCRYPTION_SECRET — KEK material for scoped sub-key secrets at rest.
  • the cold-object tiering family (OPENBUCKET_TIER_*).

When embedding, set these through process.env (or your app's config) even though they are not part of the forRoot options object.

OpenBucketModule.forRoot({
dataDir: '/var/lib/openbucket',
rootCredentials: {
accessKeyId: process.env.OB_ACCESS_KEY!,
secretAccessKey: process.env.OB_SECRET_KEY!,
},
admin: {
username: 'admin',
passwordHash: process.env.OB_ADMIN_HASH!, // argon2id
jwtSecret: process.env.OB_JWT_SECRET!,
},
});

Top-level options​

OptionRequiredDefaultNotes
dataDir✅—SQLite DB + blob payloads + generated sse.key. Created on boot if absent.
rootCredentials✅—{ accessKeyId, secretAccessKey } — the root SigV4 credential.
mountPath/storageRoute prefix for all OpenBucket routes. Path-style only; '' mounts at the root.
regionus-east-1Region reported to clients.
endpoint—DNS-safe hostname for endpoint discovery / presign defaults.
sseKeygeneratedbase64 of 32 bytes. Else generated + persisted to <dataDir>/sse.key.

admin — the admin surface​

Omit the whole block to run a headless, S3-only store (no admin API, no JWT guard, no console). A present-but-partial block is rejected at boot.

FieldRequiredDefaultNotes
username✅—Bootstrap admin login.
passwordHash✅—argon2id hash of the admin password.
jwtSecret✅—Strong secret signing admin JWTs.
serveUitrueServe the bundled Angular SPA at <mountPath>/admin.
jwtAccessTtl900Access-token TTL in seconds.
jwtRefreshTtl604800Refresh-token TTL in seconds (7 days).

limits​

FieldDefaultNotes
maxObjectSizeMb5120000Max single-object size in MiB.
maxMultipartParts10000Max parts per multipart upload.
multipartTtlHours24Abandoned-multipart TTL.

webhooks​

Omit to disable HTTP webhook delivery (in-process @OnObject* handlers still work). Presence of url enables the transactional outbox + delivery runner.

FieldRequiredDefaultNotes
url✅—Target endpoint. https required unless loopback.
secret✅—Strong secret. HMAC-SHA256 signing key.
eventsall threeSubset of object.created / object.deleted / multipart.completed.
maxAttempts8Attempts before dead-letter.
timeoutMs5000Per-request timeout.
pollMs15000Delivery tick interval.

replication​

Omit to disable. A present-but-partial block is rejected at boot (require bucket + both credentials).

FieldRequiredDefaultNotes
bucket✅—Remote target bucket (must already exist).
credentials✅—{ accessKeyId, secretAccessKey } for the target.
endpoint—S3-compatible endpoint. Omit for real AWS S3.
regionus-east-1Target region.
forcePathStyletruetrue for MinIO/S3-compat; false for AWS.
maxAttempts12Dead-letter cap.
drainIntervalMs5000Drain tick interval.
batchKeys50Distinct keys drained per tick.
largeObjectThresholdBytes67108864Stream via multipart above this (64 MiB).

backups​

Omit to disable. Exactly one of cron / intervalMinutes must be set.

FieldDefaultNotes
scopeinstanceinstance = one snapshot; buckets = one per bucket.
cron—5-field cron. Mutually exclusive with intervalMinutes.
intervalMinutes—Fixed interval (minutes). Mutually exclusive with cron.
dir<dataDir>/backupsAbsolute snapshot directory.
keepLast7Keep the newest N snapshots.
maxAgeDays30Also keep anything younger than this.
checkIntervalMs60000"Is a snapshot due?" wake tick.
pushToReplicationfalseAlso push each snapshot to the replication target.

metrics & tracing​

FieldDefaultNotes
metrics.modeoffoff / public / token — gates <mountPath>/metrics.
metrics.token—Strong bearer token — required when mode: 'token'.
tracing.enabledfalseOpenTelemetry span wrapping. No-op unless @opentelemetry/api + an SDK are present.
info
Static options in forRootAsync

mountPath, serveUi, and admin (the on/off switch) are static — passed alongside useFactory because routing is wired at module-config time. The admin secrets still come from the async factory. See the NestJS module reference.

Refuse-to-boot validation

Both entry points validate security-critical formats before serving a single request: a non-argon2id passwordHash, a too-short jwtSecret, a weak rootCredentials.secretAccessKey, a token-mode metrics endpoint behind a weak token, or an enabled-but-incomplete replication / backup block all abort startup with a precise message. Fail loud, never silently insecure.

Next steps​