Configuration
Jay takes configuration from environment variables, a YAML file, or both. When
both set the same key, the environment wins and the conflict is logged at
WARN.
Variables
Section titled “Variables”| Variable | Default | Description |
|---|---|---|
JAY_CONFIG_FILE |
(optional) | Path to a YAML config file. Also settable with --config-file, which takes precedence |
JAY_DATA_DIR |
./data |
Data directory for objects and metadata |
JAY_LISTEN_ADDR |
:9000 |
S3 API listen address |
JAY_ADMIN_ADDR |
:9001 |
Admin API listen address |
JAY_NATIVE_ADDR |
:4444 |
Native protocol listen address; empty disables the listener |
JAY_ADMIN_TOKEN |
(required) | Bearer token for the admin API; at least 32 characters |
JAY_SIGNING_SECRET |
(required) | AES-GCM key for presigned URLs and token secrets; at least 32 characters |
JAY_LOG_LEVEL |
info |
debug, info, warn or error |
JAY_TLS_CERT |
(optional) | Path to a TLS certificate for the S3 and admin listeners |
JAY_TLS_KEY |
(optional) | Path to the matching private key |
JAY_NATIVE_TLS_CERT |
(optional) | Path to a TLS certificate for the native listener. Set both or neither — one alone aborts startup |
JAY_NATIVE_TLS_KEY |
(optional) | Path to the matching private key |
JAY_RATE_LIMIT |
100 |
Requests/sec per token (0 disables) |
JAY_RATE_BURST |
200 |
Rate limit burst size; must be at least 1 — a burst of 0 rejects every request, so turn the limiter off with JAY_RATE_LIMIT=0 instead |
JAY_TRUST_PROXY_HEADERS |
false |
Trust X-Forwarded-For / X-Real-IP |
JAY_SCRUB_INTERVAL_HOURS |
6 |
Scrubber interval |
JAY_SCRUB_BYTES_PER_SEC |
52428800 |
Scrubber read throttle (0 = unlimited) |
JAY_SCRUB_MAX_PER_RUN |
100 |
Objects visited per bucket per scrub tick |
JAY_METADATA_BACKUP_DIR |
<data_dir>/backups |
Where the hourly bbolt snapshots go. Metadata only — object bytes are never copied there. Point it at a separate volume for real DR |
JAY_BACKUP_DIR |
— | Deprecated spelling of the above. Still honoured, warns at startup |
JAY_MIN_FREE_BYTES |
524288000 |
Readiness fails below this free space (0 disables) |
JAY_MAX_OBJECT_SIZE |
5368709120 |
Largest accepted body and multipart part (0 disables) |
JAY_SEED_TOKEN_ACCOUNT |
(optional) | See Seed token |
JAY_SEED_TOKEN_ID |
(optional) | Seed token ID |
JAY_SEED_TOKEN_SECRET |
(optional) | Seed token secret, bcrypt-hashed before storage |
JAY_TOKEN_ID |
(optional) | Credentials for the jay subcommands; the server ignores them |
JAY_TOKEN_SECRET |
(optional) | Same |
The two required secrets
Section titled “The two required secrets”JAY_ADMIN_TOKEN and JAY_SIGNING_SECRET have no defaults and no development
fallback. If either is missing or shorter than 32 characters, Jay exits before
opening the database and before binding a listener.
JAY_SIGNING_SECRET is the AES-GCM key that encrypts token secrets in bbolt.
Losing it makes every stored token unreadable. To change it, use
jay-rekey, which re-encrypts the stored secrets — do not just set a new value.
YAML file
Section titled “YAML file”Point Jay at a file with --config-file path/to/config.yml or
JAY_CONFIG_FILE:
data_dir: ./datalisten_addr: ":9000"admin_addr: ":9001"native_addr: ":4444"
# Secrets can reference env vars via ${VAR} interpolation, so this file can be# committed to git while the secrets stay in .env.admin_token: ${JAY_ADMIN_TOKEN}signing_secret: ${JAY_SIGNING_SECRET}
log_level: inforate_limit: 100rate_burst: 200trust_proxy_headers: false
# ${VAR:-default} provides a fallback.tls_cert: ${JAY_TLS_CERT:-}tls_key: ${JAY_TLS_KEY:-}
# The native listener has its own pair on purpose: it is not covered by# tls_cert above, and turning on TLS for S3 does not turn it on here.native_tls_cert: ${JAY_NATIVE_TLS_CERT:-}native_tls_key: ${JAY_NATIVE_TLS_KEY:-}
scrub: interval_hours: 6 bytes_per_sec: 52428800 max_per_run: 100
# Metadata snapshots only. Object bytes are not copied here or anywhere else:# https://ivangsm.github.io/jay/guides/backup-and-restore/metadata_backup: dir: ${JAY_METADATA_BACKUP_DIR:-}min_free_bytes: 524288000max_object_size: 5368709120
seed_token: account: ${JAY_SEED_TOKEN_ACCOUNT:-} id: ${JAY_SEED_TOKEN_ID:-} secret: ${JAY_SEED_TOKEN_SECRET:-}
# Credentials for the `jay` subcommands. The server never reads these.client: token_id: ${JAY_TOKEN_ID:-} token_secret: ${JAY_TOKEN_SECRET:-}Precedence is environment variable > YAML > built-in default. A conflict
logs WARN at startup but does not fail.
An empty value means “not configured”. key: "" in YAML and JAY_KEY="" in
the environment are both discarded, and the default stands.
That is deliberate. An empty value almost always comes from a template whose
variable was never set — listen_addr: ${JAY_LISTEN_ADDR}, or a compose file
passing a variable straight through — and applying it literally would move the
data directory or serve on port 80, because Go’s net/http reads an empty
Addr as :http.
The one exception is native_addr, whose empty value is the documented off
switch for the native listener.
When discarding an empty value actually overrides something, it is logged at
WARN. When the key would have been empty anyway — tls_cert,
metadata_backup.dir, seed_token.*, client.* above — it is not, because
seven lines of noise per startup is what teaches people to ignore the one that
matters.
A renamed key keeps working and says so. backup.dir / JAY_BACKUP_DIR
became metadata_backup.dir / JAY_METADATA_BACKUP_DIR, because the old name
promised a copy of the objects and only the metadata was ever copied. The old
spelling still sets the same value and logs a deprecation warning naming the new
one; dropping it would have made a deployment pointing at a separate volume fall
back to the default without a word. With both set, the new name wins.
Interpolation resolves ${VAR} and ${VAR:-default} against the
environment, on string values only. If neither is set the value ends up empty,
which then trips the secret-length check if it was admin_token or
signing_secret.
jay-config
Section titled “jay-config”jay-config ships in the release archives and the container image.
# YAML → .env (stdout if --output is omitted)jay-config yaml-to-env --input config.yml --output .env
# .env → YAMLjay-config env-to-yaml --input .env --output config.yml
# Check required secrets, seed-token atomicity and value rangesjay-config validate --input config.yml${VAR} interpolation is preserved literally during conversion. The tool moves
keys between formats; it never resolves an environment variable.