Configuration reference
Faber is configured entirely through environment variables. It checks them at
startup and refuses to start if one is malformed or out of range; the error
names the variable. Numbers must be positive unless stated otherwise. Sizes in
bytes are plain integers except MEMORY_MAX.
GET /api/v1/info reports the effective limits, profiles, cache state and
toolchains of a running instance.
Service
Section titled “Service”| Variable | Default | Meaning |
|---|---|---|
API_KEY | required | Key expected in the Authorization header (Bearer <key> or the raw key). A comma-separated list accepts every listed key, for rotation. Faber logs a warning for keys shorter than 32 characters; generate them with openssl rand -hex 32 |
HOST | 0.0.0.0 | Listen address |
PORT | 3000 | Listen port. The API is served under /api/v1 |
SHUTDOWN_TIMEOUT_MS | 5000 | How long in-flight requests may drain after SIGTERM. Keep it below the container’s stop grace period (10 s in Docker by default) |
HEADER_READ_TIMEOUT_MS | 10000 | Time a client has to send a request’s headers. It also closes idle keep-alive connections, so slow or stalled clients cannot hold connections open |
REQUEST_BODY_TIMEOUT_MS | 60000 | Time a client has to send a request body once the headers have arrived |
RATE_LIMIT_PER_MINUTE | 0 (off) | Requests per API key and minute on protected routes, as a token bucket. A request over the limit gets 429 with code rate_limited and a Retry-After header. Everyone using the same key shares its bucket |
RATE_LIMIT_BURST | one minute’s worth | Requests a key may send at once before the per-minute rate applies. Must be at least 1 when rate limiting is on |
LOG_FORMAT | text | json writes one JSON object per log line |
RUST_LOG | faber=info,faber_api=info,faber_runtime=info | Log filter (tracing EnvFilter syntax) |
Execution limits
Section titled “Execution limits”| Variable | Default | Meaning |
|---|---|---|
MAX_CONCURRENCY | 10 | Task slots: sandboxed tasks that may run at once across all requests. A request reserves one slot per task of its widest step; requests that do not fit get 503 |
MAX_PARALLEL_TASKS | 8 | Tasks in one parallel step. Must not exceed MAX_CONCURRENCY |
MAX_STEPS_PER_REQUEST | 64 | Steps in one request |
MEMORY_MAX | 256M | Per-task memory.max, in bytes or with a K, M, G or T suffix. Must be finite and at least 1M |
PIDS_MAX | 64 | Per-task pids.max (processes and threads) |
CPU_MAX | 50000 100000 | Per-task cpu.max: <quota> [<period>] in microseconds. Quota is max or at least 1000; period 1000 to 1000000 (default 100000). The default is half a core |
WALL_TIMEOUT_MS | 5000 | Wall-clock limit per task |
CPU_TIME_LIMIT_SECS | 5 | RLIMIT_CPU per task process |
OVERALL_TIMEOUT_MS | 30000 | Deadline for a whole request. Faber warns at startup when MAX_STEPS_PER_REQUEST × WALL_TIMEOUT_MS exceeds it |
OUTPUT_LIMIT_BYTES | 1048576 | Bytes kept per output stream per task; the task is killed beyond it |
REQUEST_OUTPUT_LIMIT_BYTES | 16777216 | Output bytes kept across every task of a request |
EXECUTE_BODY_LIMIT_BYTES | 1048576 | Largest /execute request body |
Sandbox
Section titled “Sandbox”| Variable | Default | Meaning |
|---|---|---|
DEFAULT_SANDBOX_PROFILE | compile_v2 | Seccomp profile for tasks that do not name one. Must be in ALLOWED_SANDBOX_PROFILES |
ALLOWED_SANDBOX_PROFILES | compile_v2,native_v2 | Profiles a request may select: any of compile_v2, native_v2, compile_v1, native_v1. See sandbox profiles |
SANDBOX_READONLY_PATHS | /bin,/lib,/lib64,/usr,/etc/alternatives,/etc/ld.so.cache,/etc/passwd,/etc/group | Paths of the image that tasks see read-only. Replaces the default list, so include the defaults when adding to it. Each must be absolute, without .., and not / or under /proc, /sys, /dev, /tmp or /faber |
SANDBOX_IDENTITY_BASE | 100000 | First host UID/GID leased to requests |
SANDBOX_IDENTITY_COUNT | 65536 | Size of that range. Several Faber instances on one kernel must use ranges that do not overlap |
A toolchain that keeps files outside the default paths needs them added, for example the JDK’s configuration:
SANDBOX_READONLY_PATHS=/bin,/lib,/lib64,/usr,/etc/alternatives,/etc/ld.so.cache,/etc/passwd,/etc/group,/etc/java-17-openjdkThe Dockerfile builder computes this list for the languages you pick.
Step cache
Section titled “Step cache”See step cache for the semantics.
| Variable | Default | Meaning |
|---|---|---|
CACHE_ENABLED | false | Turns the step cache on (true, false, 1 or 0). Off, cache flags in requests are ignored and responses report "enabled": false |
CACHE_BACKEND | memory | memory: the in-process tier only. redis: the in-process tier plus a shared Redis tier |
CACHE_REDIS_URL | none | Redis connection URL, for example redis://:password@redis:6379/0. Required when CACHE_BACKEND=redis |
CACHE_NAMESPACE | default | Prefix of every Redis key, and part of every layer key. Use one per environment sharing a Redis. DELETE /cache clears only this namespace |
CACHE_TTL_SECS | 300 | Lifetime of a cached layer, in both tiers; refreshed on every hit |
CACHE_MAX_BYTES | 67108864 | Bytes held by the in-process tier, results and file contents included; least recently used layers are evicted |
CACHE_MAX_ENTRIES | 1024 | Layers and blobs held by the in-process tier |
CACHE_MAX_LAYER_BYTES | 67108864 | Largest workspace a layer may capture. A step whose workspace is bigger runs normally but is not stored |
CACHE_MAX_LAYER_FILES | 4096 | Most files and directories a layer may capture |
Image and toolchains
Section titled “Image and toolchains”| Variable | Default | Meaning |
|---|---|---|
FABER_TOOLCHAINS | empty | The language toolchains installed in the image, as comma-separated id@version or id entries, for example c@12,python@3.12,node. Reported by GET /info and part of every cache key. Images from the Dockerfile builder set it |
FABER_IMAGE_ID | empty | An identifier of the image build, such as its digest or a CI build ID. Part of every cache key, so a rebuilt image never replays layers produced by different toolchains. Set it when the cache is enabled |
File store
Section titled “File store”See file store.
| Variable | Default | Meaning |
|---|---|---|
FABER_STORE_BACKEND | memory | memory, filesystem or hybrid |
FABER_STORE_PATH | /var/lib/faber/store | Directory of the filesystem and hybrid backends. Needs a writable volume, since the production container’s root is read-only |
FABER_STORE_MAX_TOTAL_BYTES | 536870912 | Total bytes of stored files; uploads beyond it are refused |
FABER_STORE_MAX_ENTRIES | 1000 | Number of stored files; uploads beyond it are refused |
FABER_STORE_TTL_SECS | 3600 | Files expire this long after their last upload or read. 0 disables expiry |
FABER_STORE_TTL_CHECK_SECS | 60 | Interval of the sweep that removes expired files |
FABER_STORE_MAX_MEMORY_ENTRIES | 1000 | hybrid only: files cached in memory |
FABER_STORE_MAX_MEMORY_SIZE | 104857600 | hybrid only: bytes cached in memory |
UPLOAD_FILE_LIMIT_BYTES | 52428800 | Largest single upload (413 above it) |
MAX_CONCURRENT_UPLOADS | 4 | Uploads buffered at once (503 above it) |
How the limits relate
Section titled “How the limits relate”Aggregate resources. Faber’s own cgroup is capped at
(MEMORY_MAX + supervisor allowance) × MAX_CONCURRENCY bytes and
(PIDS_MAX + 3) × MAX_CONCURRENCY processes, which covers every admitted task
at its own limit together with the root processes that run it. The supervisor
allowance is 64 MiB + 2 × REQUEST_OUTPUT_LIMIT_BYTES (output capped at
1 GiB) plus the 288 MiB the workspace and /tmp tmpfs can hold (their data
and 1 KiB for each of their inodes). Each request runs in
its own cgroup capped at its widest step times the per-task limits plus that
allowance. Sizing turns this into container and host
memory.
Deadlines. Each step’s wall timeout is clipped to what is left of
OVERALL_TIMEOUT_MS, and steps that cannot start are returned as
not_started. With the defaults, a request of 64 slow steps (64 × 5 s) cannot
finish within 30 s; that is intended, and the startup warning says so.
Memory per request. A response holds at most REQUEST_OUTPUT_LIMIT_BYTES
of task output. JSON escaping can grow control bytes up to six times, and the
result exists in the sandbox controller, the API process and (with the cache)
the cache, so budget roughly 6 × REQUEST_OUTPUT_LIMIT_BYTES per copy per
concurrent request.
Fan-out. MAX_PARALLEL_TASKS must not exceed MAX_CONCURRENCY, or the
widest allowed step could never be admitted.