Skip to content
Noite
Esc
↑↓navigate↵open⌘Jpreview
On this page

Environment variables

Every runner env var with its default, effect, and owner.

Defaults below are what docker/compose.yaml applies when the var is unset. The runner refuses dev secrets off a localhost base. A set-but-malformed numeric value refuses the boot instead of falling back: the runner exits naming each bad var, its value and the expected form — unset (or empty) still means the default.

Ownership: runner-owned (read by the runner) vs worker-passthrough (handed to the control UI worker untouched).

These are install-level variables (Compose, the image environment). Per-app values live in each app’s Settings → Environment instead: they reach the app’s install, build, release command, fleet, and Worker code (env.NAME), and a change applies from the next deploy. See Configure.

Image

Var Default Effect Owner
NOITE_IMAGE ghcr.io/ryuzcorp/noite:alpha Image Compose pulls: a release channel (alpha, stable) or a version tag Compose

Domain and edge

Var Default Effect Owner
BASE_DOMAIN noite.localhost Tenant + control DNS base runner-owned
CONTROL_SUBDOMAIN (empty) Empty = control on bare domain; prod sets app runner-owned
CONTROL_EXTRA_HOSTS (empty) Comma-separated extra control hostnames runner-owned
CADDY_AUTO_HTTPS unset = on except a localhost base off behind a terminating proxy runner-owned
HTTP_PORT 9080 Host HTTP port runner-owned
HTTPS_PORT 9443 Host HTTPS port runner-owned
GIT_PUBLIC_BASE http://git.noite.localhost:9080 / https://git.<domain> Public Git remote base runner-owned
BETTER_AUTH_URL http://noite.localhost:9080 Control origin; set before first registration (passkeys bind to it) worker-passthrough
CADDYFILE_PATH /data/caddy/Caddyfile Generated Caddyfile location runner-owned
CADDY_ACCESS_LOG /data/caddy/access.log JSON access log tailed for device/path/ref stats runner-owned
CADDY_ADMIN_URL http://127.0.0.1:2019 Caddy admin endpoint for /load writes runner-owned
RUNNER_BIND 0.0.0.0:8080 Runner listen address runner-owned
RUNNER_WORK_DIR /data/runner Fleet working dirs, mirrors, builds runner-owned
RUNNER_POLL_MS 5000 Reconcile tick interval runner-owned
CELLD_BIN celld Fleet binary path runner-owned
CONTROL_BUNDLE_DIR /opt/noite/control/dist Baked control UI bundle (fleet #0) runner-owned
RUST_LOG noite_runner=info,tower_http=info Runner log filter runner-owned
CELLD_ADVERTISE RAILWAY_PRIVATE_DOMAIN, else the container hostname Address other control nodes dial during an overlapping redeploy (:8091); must be reachable from a peer runner-owned

Auth

Var Default Effect Owner
RUNNER_TOKEN dev-runner-token Bearer token for runner API + UI server calls; refused off a localhost base at default runner-owned
BETTER_AUTH_SECRET dev-only-change-me-to-a-long-random-string Session signing secret; dev- values refused on real domains worker-passthrough
NOITE_ADMIN_EMAIL (empty) Only this address may claim the first admin account, and it must still present the one-time setup code (asked by the installer; kept on re-runs). Also promotes the matching account to admin at startup. Empty on a public base means the first registration needs the code alone instead worker-passthrough
NOITE_EMAIL_WEBHOOK_URL (empty) Receives lost-passkey OTP JSON; unset = printed to the control log on a localhost base, not sent (the failure is logged) on real domains. Without it, use noite-runner recover worker-passthrough
NOITE_SMTP_FROM (empty) OTP sender label worker-passthrough

Store

Var Default Effect Owner
NOITE_S3_BUCKET noite Single bucket; git/, fleets/, control/ prefixes runner-owned
S3_ENDPOINT http://rustfs:9000 Object-store endpoint runner-owned
S3_PUBLIC_ENDPOINT http://127.0.0.1:9000 Endpoint returned to host-side tools runner-owned
AWS_REGION us-east-1 Store region runner-owned
AWS_ACCESS_KEY_ID RUSTFS_ACCESS_KEY → noiteaccess Store key runner-owned
AWS_SECRET_ACCESS_KEY RUSTFS_SECRET_KEY → 32-char dev default Store secret runner-owned
RUSTFS_ACCESS_KEY noiteaccess Bundled RustFS key runner-owned
RUSTFS_SECRET_KEY 32-char dev default Bundled RustFS secret runner-owned
RUSTFS_API_PORT 9000 Bundled RustFS S3 port (loopback) runner-owned
RUSTFS_CONSOLE_PORT 9001 Bundled RustFS console port runner-owned
NOITE_DB /data/noite.sqlite (image) Runner SQLite path (sqlite: prefix optional) runner-owned

Tenancy

Var Default Effect Owner
NOITE_TENANCY unset = multi off a localhost base, single on multi sandboxes builds/fleets as users behind egress policy; single = operator pushes only runner-owned
NOITE_CREDENTIAL_PROVIDER auto Where tenant fleet keys come from: auto scopes the bundled RustFS and probes other stores for the MinIO admin API, minio forces that provider (MinIO, RustFS), aws forces the AWS IAM provider (needs an IAM-capable key), root keeps the root keys explicitly runner-owned
NOITE_SANDBOX unset = on when the engine socket answers Sandbox containers (celld containers classes, e.g. the agent template); 0, false or off disables them: no relay, no per-fleet engine sockets, fleets get no DOCKER_HOST, and deploys declaring containers fail runner-owned
RUNNER_BUILD_UID_BASE / RUNNER_BUILD_UID_RANGE 10030 / 1024 Reserved per-app build uid range [base, base + range); each app’s builds and release commands run as their own uid from it runner-owned
RUNNER_FLEET_UID / RUNNER_FLEET_GID 10020 Tenant fleet user runner-owned
NOITE_FLEET_PORTS 20000-29999 Fleet port range, two ports per app runner-owned
NOITE_STOP_BUDGET_MS 25000 Graceful-stop budget (min 5000); fleets stop inside budget − 3 s runner-owned
RUNNER_BUILD_MAX_MB 2048 Refuse builds past this worktree size (MiB) runner-owned
RUNNER_BUILD_TIMEOUT_S 1800 Bound on each install and build step (seconds) runner-owned
RUNNER_CI on Run a repo-root noite.ci.yaml in containers instead of the built-in install/build; 0/false/off disables runner-owned
RUNNER_CI_IMAGE oven/bun:1.4.4 Image for a CI job that names none runner-owned
RUNNER_CI_IMAGES the default image only Comma-separated exact image allowlist; the default image is always allowed. Use a tag or digest runner-owned
RUNNER_CI_NETWORK bridge Docker network for CI containers: bridge (registry access) or none runner-owned
RUNNER_CI_DOCKER_BIN docker Docker CLI binary for CI containers runner-owned

Bounds

Var Default Effect Owner
RUNNER_MAX_APPS_PER_USER 10 Per-account app quota, enforced on create runner-owned
RUNNER_MAX_ENVIRONMENTS_PER_APP 5 Preview environments per app, enforced at create runner-owned
RUNNER_MAX_ENVIRONMENTS 25 Preview environments per instance, enforced at create runner-owned
RUNNER_FLEET_MAX_RSS_MB 0 → CELLD_MAX_RSS_MB when non-zero; container-wide shed threshold by design (celld measures container-scale working set), never per-fleet; 0 = celld default (80% of container memory) worker-passthrough (fleet)
RUNNER_FLEET_MEMORY_MB 512 Per-fleet memory.max in MiB (own cgroup per fleet); per-app override in Settings → Limits; needs the multi-only cgroup grant (docker/compose.multi.yaml) — without it fleets share the container cgroup runner-owned
RUNNER_FLEET_MEMORY_MAX_MB default cap Ceiling for a per-app memory override in MiB; in multi a tenant can only lower its cap, never raise it past this. Single installs ignore it runner-owned
RUNNER_FLEET_IDLE_EVICT_S 120 → CELLD_IDLE_EVICT_S; idle seconds before a fleet hibernates cells worker-passthrough (fleet)
RUNNER_FLEET_LEASE_TTL_MS 60000 → CELLD_TTL_MS on every fleet and the control node; a storage or disk stall longer than two thirds of it restarts every app. Minimum 10000 worker-passthrough (fleet)
RUNNER_FLEET_DEPLOY_POLL_S 300 → CELLD_DEPLOY_POLL_S; covers missed /reload only worker-passthrough (fleet)
RUNNER_FLEET_ASSET_CACHE_MB 64 → CELLD_ASSET_CACHE_BYTES; per-fleet on-disk asset cache worker-passthrough (fleet)
RUNNER_FLEET_LOCAL_CACHE_MB 1/16 of detected memory, clamped 64–512 → CELLD_LOCAL_CACHE_MAX_BYTES; per-fleet hibernated-DB cache (celld’s own default is 2 GiB per node) worker-passthrough (fleet)
RUNNER_CONTROL_MEMORY_MB 1/4 of detected memory, clamped 256–1024 Control fleet (fleet #0) cap in MiB; its own cgroup leaf when the install has writable cgroups, else the container-wide shed threshold runner-owned
RUNNER_FLEET_SPAWN_BATCH 1 on ≤ 2 cores, else 2 Cold fleets started per reconcile pass, so a boot or upgrade does not stack every celld at once; restarts are never batched runner-owned
RUNNER_BUILD_CONCURRENCY 1 on ≤ 2 cores, else 2 Concurrent tenant deploys; one slot covers install, build, release and the celld deploy runner-owned
RUNNER_BUILD_MEMORY_MB detected memory minus a 1 GiB resident floor (a quarter of bigger boxes), split across build slots, clamped 512–4096 memory.max of each deploy’s build cgroup leaf, which runs install, build and release; 0 leaves builds uncapped. Over the cap the leaf may swap up to RUNNER_BUILD_SWAP_MB, then the step is killed alone with a log naming both budgets — fleets and the runner keep serving. A Vite 8/Rolldown build of a Void app peaks around 1.3–1.5 GiB: on a box that cannot spare that in RAM the swap carries the overflow, and a box with no swap or a build that fills both still fails with the budget named. The leaf also gets a hard CPU quota (one core less than the box, split across concurrent build slots) and a low I/O weight, so a build cannot starve the runner, RustFS or SSH runner-owned
RUNNER_BUILD_SWAP_MB the host’s swap split across the build slots, clamped 0–4096; 0 when the host has none memory.swap.max of each deploy’s build leaf. The overflow past the RAM cap, so a build larger than the box’s spare RAM finishes (slowly) instead of thrashing at the cap until the step timeout; the watchdog only fails it once RAM and swap are both full. Fleets keep zero swap. An explicit 0 restores the old zero-swap behavior runner-owned
RUNNER_PRESSURE_PCT 85 Container memory percentage that triggers pressure parking of idle apps; 0 disables runner-owned
RUNNER_PRESSURE_IDLE_S 300 How long an app must be idle before pressure may park it runner-owned
RUNNER_PRESSURE_SWEEP_S 30 Pressure check interval (minimum 10) runner-owned
RUNNER_OTEL_FLUSH_MS 30000 → CELLD_OTEL_FLUSH_MS; bucket-flush + ingest tick cadence (min 1000); dashboards lag ~40 s worker-passthrough (fleet)
RUNNER_BUILD_CACHE_MB 1024 Per-app persistent build cache cap (package managers and their caches); 0 disables runner-owned
RUNNER_TELEMETRY_RETENTION_DAYS 30 Days kept in bucket prune, metric tables, glob floor (min 1; a non-integer or < 1 value fails boot validation) runner-owned
RUNNER_TIP_SWEEP_S 60 Fallback S3 tip sweep per app; covers out-of-band bundle writes runner-owned
RUNNER_DEPLOY_PROBE_PATH / Pre-activation canary: after staging, the deploy asks the staged generation for HEAD <path> through celld invoke before the one activate serves it; an answer below 500 moves the pointer, anything else fails the deploy with the old generation still serving. Empty disables the probe (a celld without invoke) runner-owned
RUNNER_DEPLOY_PROBE_TIMEOUT_S 30 Seconds the canary waits for the staged generation runner-owned
RUNNER_FLEET_LOG error,celld=warn Fleet RUST_LOG (runner’s own filter stays separate) worker-passthrough (fleet)

Sleep

Var Default Effect Owner
RUNNER_SLEEP_AFTER_MIN detected memory: 15 (≤ 2 GiB), 360 (≤ 4 GiB), else 1440 Park apps idle this many minutes; wins over RUNNER_SLEEP_AFTER_H; 0 = never (no per-app override re-enables it), clamped at 2592000 s (30 days) runner-owned
RUNNER_SLEEP_AFTER_H — The same threshold in whole hours, for windows minutes cannot express; ignored when RUNNER_SLEEP_AFTER_MIN is set; 0 = never (overrides a set RUNNER_SLEEP_AFTER_MIN); clamped at 720 h (30 days) runner-owned
RUNNER_SLEEP_SWEEP_S a quarter of the threshold, clamped 300–3600 Sleep sweep cadence runner-owned
RUNNER_WAKE_TIMEOUT_S 120 Longest a held request waits for a woken fleet; failed wake answers 503 runner-owned
RUNNER_WAKE_CONCURRENCY 4 Apps allowed to cold-start at once for requests; later wakes queue inside RUNNER_WAKE_TIMEOUT_S runner-owned
See Limits for the sleep mechanism.

Telemetry and rate limits

Var Default Effect Owner
NOITE_TELEMETRY unset = on Anonymous instance heartbeat is opt-out; 0, false or off disables it and locks the setting (Telemetry) runner-owned
DO_NOT_TRACK unset = on Any non-empty value other than 0 disables the heartbeat and locks the setting, honouring the Do Not Track convention runner-owned
NOITE_UPDATE_CHECK unset = on Daily release check is opt-out; 0, false or off disables it and locks the setting (Updating) runner-owned
NOITE_RATE_LIMIT_RPM 600 Worker per-route-class (auth/invite) limit; 429 + Retry-After; 0 disables worker-passthrough
NOITE_AUTH_RATE_LIMIT 600 better-auth per-client /api/auth/* budget; 0 disables worker-passthrough
NOITE_EDGE_RPM 1800 Edge limit per client IP, per site (control, API, fallback page, each app across its hostnames); 0 disables runner-owned (Caddy)
NOITE_EDGE_GIT_RPM 120 Edge limit per client IP on git.; 0 disables runner-owned (Caddy)
NOITE_EDGE_APP_RPM 0 Edge ceiling per app across every client; 0 = none runner-owned (Caddy)
NOITE_TRUSTED_PROXIES unset Proxies whose forwarded client address the edge believes: cloudflare, private_ranges, IPs/CIDRs runner-owned (Caddy)

See Edge protection for how these fit together.

Void proxy

Var Default Effect Owner
NOITE_VOID_EMAIL_DOMAIN (empty) Sender domain every Void from is pinned to (<slug>[+tag]@<domain>); required for email runner-owned
NOITE_VOID_SMTP_HOST (empty) SMTP relay host; email is enabled iff set runner-owned
NOITE_VOID_SMTP_PORT (empty) Relay port; unset = 587/465/25 by mode runner-owned
NOITE_VOID_SMTP_MODE starttls starttls, implicit or plain runner-owned
NOITE_VOID_SMTP_ALLOW_PLAINTEXT (empty) 1 allows plain (the proxy refuses it otherwise) runner-owned
NOITE_VOID_EMAIL_ALLOWLIST (empty) Admission: comma/space-separated exact addresses or @domain exact domains (subdomains do not match); empty fails closed runner-owned
NOITE_VOID_AI_ENABLED (empty) 1 turns /ai/* on, else every AI call answers 501 runner-owned
NOITE_VOID_AI_MODELS (empty) Model-map JSON for /ai/run: { "<model>": { "endpoint", "model" } } runner-owned
NOITE_VOID_AI_ALLOW_PLAINTEXT (empty) 1 allows http model endpoints, loopback sidecars only (the runner refuses http otherwise; inert in multi tenancy, where loopback endpoints never boot) runner-owned
NOITE_VOID_SANDBOX_MAX_CONCURRENT 4 Max concurrent live sandboxes per Void fleet (0 admits none) runner-owned

Unset means email sends and AI calls answer 501; /revalidate needs no operator config. Per-app credentials (the SMTP login, the AI endpoint key) are tenant Settings, never operator config — see Void email and AI. Misconfigured values refuse the boot naming the var. In multi tenancy the relay host and AI endpoints must be publicly reachable — loopback, private, link-local, CGNAT and ULA addresses, and obviously local names (localhost, *.local, *.internal, single-label names), refuse the boot naming the var.

Platform-only

Set by the platform, not by operators. Railway: PORT (listen port), RAILWAY_DEPLOYMENT_ID, RAILWAY_DEPLOYMENT_DRAINING_SECONDS (must exceed the stop budget + 5 s). TINI_SUBREAPER is set by the image’s init. None of these go in .env.

Was this page helpful?