Configuration reference
Fadebox is configured with environment variables on the app container. Everything here has a working default except the two encryption keys, which the app refuses to start without.
Each variable is the uppercased, underscore form of an underlying property — fadebox.git.cache-dir
is FADEBOX_GIT_CACHE_DIR. Durations are ISO-8601 (PT30S, PT5M) unless noted.
Most of what an operator actually configures — runtimes, ingress domains, identity providers, users, groups — is not here. Those are managed in the UI and stored in the database.
Required
| Variable | Notes |
|---|---|
QUARKUS_HTTP_AUTH_SESSION_ENCRYPTION_KEY | Encrypts the session cookie. At least 16 characters; generate with openssl rand -base64 32. Startup fails without it. Changing it signs everyone out. |
FADEBOX_ENCRYPTION_KEY | Encrypts stored credentials at rest — see Secrets at rest. At least 16 characters; generate with openssl rand -base64 32. Startup fails without it. Deliberately a different secret than the session key: changing it makes every stored credential unreadable until re-entered, so keep it stable and back it up. |
QUARKUS_DATASOURCE_JDBC_URL | e.g. jdbc:postgresql://db:5432/fadebox. |
QUARKUS_DATASOURCE_USERNAME / QUARKUS_DATASOURCE_PASSWORD | Database credentials. The schema is created and migrated on startup. |
General
| Variable | Default | What it does |
|---|---|---|
FADEBOX_PUBLIC_URL | (unset) | The address users open this installation on. Required for the ingress sign-in flow, which sends a visitor of a gated instance URL here to sign in; a gate cannot be turned on without it. When set, it is also where every managed ingress stack reaches Fadebox, so it must not be a loopback address. Unset, a stack works that address out from its daemon, and installing or updating a stack is refused where that cannot work: Fadebox in a container with a remote (tcp://) runtime — the shipped compose file with a remote static runtime — or Fadebox listening on a loopback address only. Set it then, and also when NAT, a port-forward or Fadebox published on a port other than 8080 makes the worked-out address wrong; the stack's poll status shows that case. Set, it always wins and nothing is probed. |
FADEBOX_DOCKER_HOST | unix:///var/run/docker.sock | The daemon the seeded local runtime points at. Additional runtimes are configured in the UI, not here. |
FADEBOX_SECURITY_BOOTSTRAP_ADMIN_ENABLED | true | Creates the admin account with a generated password, logged once, when it is missing. Turn off once you manage accounts another way. |
The server's time zone does not matter. Every timestamp is stored and served as an
instant — a moment with its offset spelled out — so nothing a viewer sees, and no expiry
countdown, depends on the zone the process happens to run in. The only thing that zone reaches
is the log output: set TZ on the container if you want logs to read in local time rather than
UTC.
Sessions
How long a signed-in browser session survives without a request — see Sessions. Both values state the same thirty minutes on purpose, so the browser stops presenting the cookie exactly when the server stops accepting it; change them together or not at all.
| Variable | Default | What it does |
|---|---|---|
QUARKUS_HTTP_AUTH_FORM_TIMEOUT | PT30M | The idle expiry the server enforces, slid forward by any request more than a minute after the last one. |
QUARKUS_HTTP_AUTH_FORM_COOKIE_MAX_AGE | PT30M | How long the browser keeps presenting the cookie. Shorter than the timeout and a session ends early; longer and an idle tab returns a 401 on a cookie the server has already written off. |
Single sign-on sessions are held to the same thirty minutes, renewed with the provider behind the scenes — but that window is fixed, not read from these variables, and is bounded by the provider's own session policy. Lengthening the form window therefore lengthens only the form window.
Sign-in hardening
Two limits on guessing a credential — see Guessing is expensive. Neither changes what a refused caller sees: a locked or throttled attempt answers exactly as a wrong password does, and the audit log is where the difference is written.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_AUTH_LOCKOUT_THRESHOLD | 5 | Consecutive failed sign-ins that lock a password account. 0 switches locking off, for an installation whose WAF already does this. |
FADEBOX_AUTH_LOCKOUT_WINDOW | PT1M | How long the lock holds. Flat, not escalating, and every lock costs a fresh threshold of failures — raising it buys brute-force resistance and sells availability in the same proportion, since a stranger who knows a username can hold the account shut for as long as they keep failing. |
FADEBOX_AUTH_THROTTLE_BURST | 10 | Failed credential checks — sign-ins, API keys, agent tokens — one client address may make before it is refused without lookup. Per node, forgotten on restart. 0 switches it off. |
FADEBOX_AUTH_THROTTLE_PER_MINUTE | 10 | How fast that allowance refills. |
The throttle keys on the address that opened the connection, not on a forwarded one, until
QUARKUS_HTTP_PROXY_TRUSTED_PROXIES below names the proxy — an address a caller can write in a
header is not a rate-limit key. Behind a proxy that leaves every client sharing the proxy's one
bucket, which is one more reason to set it.
Reverse proxy
Fadebox serves plain HTTP and expects a TLS-terminating proxy in front of it. It honours
X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-For so that the session cookie is marked
Secure, sign-in redirects keep the browser's scheme and host, and the audit log records the real
client address. Forwarding itself is on by default and needs no variable — what it needs is a proxy
that actually sends the headers. See the Reverse proxy guide for what
to send and for worked Traefik and HAProxy configurations.
| Variable | Default | What it does |
|---|---|---|
QUARKUS_HTTP_PROXY_TRUSTED_PROXIES | (unset) | The proxy's own address (IP or CIDR, comma-separated). Unset, every peer is trusted: anything that can reach the HTTP port by another route can choose the scheme and host Fadebox advertises and the client address it writes to the audit log. Set it whenever the port is reachable other than through the proxy — with 8080 published, it generally is. Fadebox logs a warning at startup while this is unset. |
QUARKUS_HTTP_PROXY_ENABLE_FORWARDED_PREFIX | false | Turn on only when Fadebox is mounted under a sub-path rather than at the root, and have the proxy send X-Forwarded-Prefix. |
QUARKUS_HTTP_PROXY_PROXY_ADDRESS_FORWARDING | true | Whether the forwarding headers are honoured at all. Turn it off only when nothing sits in front of Fadebox — with it off the server reads the socket, so no header can influence what it says about itself. |
Deploys
| Variable | Default | What it does |
|---|---|---|
FADEBOX_DEPLOY_POOL_SIZE | 4 | How many deploys run concurrently across the whole installation. |
FADEBOX_DEPLOY_START_CONCURRENCY | 4 | How many services within one deploy may start at once (pull, create, health wait). Parallel pulls can saturate a daemon's disk or network, so lower it on a constrained host; 1 makes starts fully sequential. |
FADEBOX_DEPLOY_READY_TIMEOUT | PT5M | Ceiling on waiting for readiness — both the per-wave waits and the final aggregation. |
FADEBOX_DEPLOY_CANCEL_GRACE | PT30S | How long a cancel waits for the interrupted deploy worker to return before tearing containers down anyway, so teardown cannot race a worker still creating them. |
System images
The images Fadebox pins for itself — the managed ingress stack — pull from Docker Hub as shipped. An air-gapped installation mirrors them into a registry its runtime hosts can reach and points Fadebox at it with one prefix, normally set on Administration → System. This variable is the same setting for an installation provisioned before anyone signs in; the page shows it as the value in force until an admin saves one there.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_IMAGE_REGISTRY | (unset) | A registry prefix, not a URL — registry.corp:5000/mirror — prepended verbatim to each pinned reference, so traefik:v3.7 is pulled as registry.corp:5000/mirror/traefik:v3.7. A value saved on the System page overrides it until reset; unset with nothing saved means the pins as shipped. Covers Fadebox's own images only — what your templates name is never rewritten. Read at the next ingress install, no restart. |
Egress proxy
The reverse proxy section above is about traffic arriving. This one is about traffic leaving. On a network that only reaches the outside through a proxy, Fadebox has five outbound paths — the OIDC discovery probe, the OIDC client's own calls (discovery, JWKS, the code exchange), git, the elastic runtime form's address echo, and the Google Compute Engine provider's calls to Google for an elastic runtime — and they read one setting between them, credentials included.
| Variable | Default | What it does |
|---|---|---|
QUARKUS_PROXY_HOST | (unset) | The proxy's host name. Unset means nothing is proxied, which is the default and the only behaviour an installation without a proxy sees. |
QUARKUS_PROXY_PORT | 3128 | Applies only when a host is set. |
QUARKUS_PROXY_USERNAME / QUARKUS_PROXY_PASSWORD | (unset) | For a proxy that authenticates. |
QUARKUS_PROXY_NON_PROXY_HOSTS | (unset) | Hosts to reach directly, comma-separated, with * allowed at either end of a pattern — *.internal,10.*. |
Setting the host is the whole answer; the rest are refinements. A proxy that asks for Basic
credentials needs one more thing for git and for Compute Engine, which both reach their hosts
through an HTTPS tunnel: the JVM refuses to send Basic credentials through a tunnel unless it is
told otherwise, and Fadebox leaves that default alone. Set
JAVA_OPTS_APPEND=-Djdk.http.auth.tunneling.disabledSchemes= on the app container if your proxy
wants Basic. Without it, Compute Engine calls fail with a sentence naming the proxy, not the
service-account key. One caveat worth knowing: the
OIDC client takes a proxy per identity provider and has no notion of exceptions, so a provider
whose issuer matches non-proxy-hosts is simply built without a proxy rather than being asked to
bypass one.
The managed ingress stack inherits the setting. With a host set, installing the stack on a
runtime starts its Traefik container with HTTP_PROXY, HTTPS_PROXY and NO_PROXY, so a
Let's Encrypt certificate can be issued from a proxied network — see
TLS. NO_PROXY keeps the socket proxy and the private address ranges
direct, because Traefik reaches its backends through the same setting, and then carries
QUARKUS_PROXY_NON_PROXY_HOSTS in the form Traefik reads: exact hosts, addresses, CIDR blocks
and *.suffix patterns as they are, a 10.*-style prefix as the CIDR block it means, and any
other wildcard dropped with a warning in the server log. A daemon whose bridge networks lie outside
the private ranges (100.64.0.0/10) needs that block listed there too. Re-apply the stack after
changing the proxy; pulling the stack's images through one is the daemon's own configuration.
Instance expiry
Every deploy stamps an instance with an expiry and a sweep stops what has run out — see expiry. The lifetime itself is set per environment; these two are the installation-wide default and the sweep's cadence.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_INSTANCE_DEFAULT_TTL | P7D | The lifetime environments that set none of their own inherit. PT0S turns the default off, so those instances never expire — an environment with a lifetime of its own still expires. |
FADEBOX_INSTANCE_EXPIRY_SWEEP_INTERVAL | PT1M | How often the sweep runs, and so how closely a stop follows the expiry. Also the delay before the first sweep after a restart. |
Instance health
How fadebox keeps each running instance's health current — see that section. Health is derived from the daemon and held in memory, not stored, so nothing here needs a database and a restart simply re-reads. The daemon's event stream does most of the work; the sweep is the safety net behind it.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_INSTANCE_HEALTH_EVENTS_ENABLED | true | Subscribe to each runtime's Docker event stream, so a container dying updates health within a second or two instead of at the next sweep. Set to false to rely on the sweep alone. |
FADEBOX_INSTANCE_HEALTH_SWEEP_INTERVAL | PT5M | How often the backing sweep re-reads every runtime — the net that catches what the event stream can't (a container that died while fadebox was down, a stream that is down, a runtime added late). Set to off to stop the timer; opening an instance's status and the runtime's Refresh instances button still observe on demand. |
FADEBOX_INSTANCE_HEALTH_SWEEP_DELAY | PT5S | The delay before the first sweep after a restart, kept off the startup path so an unreachable runtime cannot hold up boot. |
Runtime usage
How fadebox keeps what each runtime is costing current. Like health, it is read from the daemon and held in memory rather than stored, so nothing here needs a database and a restart simply re-reads. Two passes, because the daemon charges very differently for them: CPU and memory are cheap, and disk makes it walk every image layer and volume on the host.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_RUNTIME_USAGE_SWEEP_INTERVAL | PT1M | How often CPU and memory are re-read for every managed container. This is also the window CPU is averaged over, since the figure is the change between two readings, and the time one pass may take: containers not read by then are skipped until the next pass, and the server log says how many. Takes the short form (2m) as well. Set to off to stop collecting: every usage figure then reads as unknown, and the bars and columns show a dash. |
FADEBOX_RUNTIME_USAGE_SWEEP_DELAY | PT15S | The delay before the first pass after a restart, kept off the startup path so an unreachable runtime cannot hold up boot. |
FADEBOX_RUNTIME_DISK_SWEEP_INTERVAL | PT15M | How often the daemon is asked what it is storing — image, container and volume totals, and each instance's own volumes. Rare on purpose: the daemon walks every layer to answer, which takes seconds on a busy host. Set to off and the disk figures stay empty; the CPU and memory ones are unaffected. |
Both passes read every enabled runtime concurrently, so one slow or unreachable daemon delays only its own figures, and both bound how many calls they make at once — a runtime serving hundreds of stats requests is a runtime that has stopped serving deploys.
The Prometheus exporter publishes these same readings, so these two intervals are also how fresh a scrape can be. It is switched on in Administration → System, not here — it is an application setting rather than a variable, because an admin flips it without a restart.
Image reclaim
The one knob behind removing unused images. Retention itself is a per-runtime setting in the UI, not a variable, because a laptop and an air-gapped host want different answers.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_RUNTIME_IMAGE_MINIMUM_AGE | PT5M | How old an image must be before any door or the retention timer may remove it. It exists so a reclaim cannot take the image a deploy has just pulled but not yet created a container from. Raise it on a host where pulls are slow; there is little reason to lower it. |
Elastic machines
How Fadebox moves the virtual machines of an elastic runtime along — created on deploy, switched off on stop, destroyed on delete. A periodic pass does that rather than a worker parked on the deploy, which is what lets an instance keep waiting across a restart. How long a stopped machine keeps its disk is the runtime's own setting, not a variable. Most of the variables below are cadences, and an installation with no elastic runtime pays a single query per pass for all of them; the last two are Detect's echo service and the default boot image.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_MACHINE_SWEEP_INTERVAL | PT10S | How often each machine's state is moved one step toward what was asked of it — ask the provider for a machine, poll for an address, ping the daemon it booted, ready. Also how long a waiting deploy sits between steps, which is why it is seconds. Do not set it to off where elastic runtimes exist: machines would keep billing with nothing left to advance or destroy them. |
FADEBOX_MACHINE_PROVISION_TIMEOUT | PT15M | How long a machine may spend on one step of coming up — being asked for, booting, or coming back after a stop. Past it the waiting instance goes to ERROR with what the machine was waiting for. Per step, so a machine that is slow at each one but keeps finishing them is not failed; what this ends is a step that stopped moving. It is also how long a stop or destroy the provider accepted is waited on: past it, a machine the provider still reports as up is flagged REFUSED and the call is made again after FADEBOX_MACHINE_REFUSED_RETRY_INTERVAL. Raise it for an image that takes a long time to boot. There is no off — a machine nothing gives up on is an instance waiting for ever. |
FADEBOX_MACHINE_READY_CHECK_INTERVAL | PT1M | How often machines that are already up are asked about, which is the only way a spot machine the cloud reclaimed, or one stopped in the provider's console, is ever noticed. At most fifty per scan, oldest first and continuing where the last scan stopped, so a large fleet costs a bounded number of provider calls. |
FADEBOX_MACHINE_RETENTION_CHECK_INTERVAL | PT1H | How often switched-off machines are checked against their runtime's stopped-machine retention. Only the cadence: once an hour is already far finer than a retention measured in days. At most fifty are given up per scan. |
FADEBOX_MACHINE_REFUSED_RETRY_INTERVAL | PT5M | How long a machine whose stop or destroy the provider refused — or accepted and did not carry out within FADEBOX_MACHINE_PROVISION_TIMEOUT — is left alone before that call is made again — otherwise a refusal that retrying will not fix would be six calls a minute for ever. Neither call is ever escalated into destroying the machine. A transient refusal is not this; that is retried on the very next pass. Raise it on a provider that rate-limits hard; there is no off, because a machine nothing asks about again is a bill nobody ends. |
FADEBOX_MACHINE_RECONCILE_INTERVAL | PT10M | How often each elastic runtime's machines are listed at the provider and compared with Fadebox's rows — one listing per runtime, never one per machine. The pass attaches a lost handle back and reports everything else; nothing is destroyed by it. off stops the periodic pass, and asking for one on demand still works. Never gated on the licence. |
FADEBOX_MACHINE_RECONCILE_GRACE | PT10M | How young a machine at the provider may be and still not be reported as an orphan. A machine exists at the provider for a moment before Fadebox has finished recording that it does, so without this every deploy in progress would read as a leak. It is also what a reclaim refuses a machine for being younger than, so what a pass hides and what will not be destroyed are one rule. It bounds orphans only: a machine belonging to another installation is reported whatever its age, and a provider that cannot say how old a machine is gets it reported with the note ageUnknown rather than hidden for ever — and a reclaim then asks for that one to be confirmed by hand. |
FADEBOX_EGRESS_ECHO_URL | https://api4.ipify.org | The echo service the elastic runtime form's Detect asks for this installation's public address — what machines reached across the internet see Fadebox coming from. Asked only when an admin clicks, through the egress proxy if there is one, and never on the server's own initiative. The echo must answer with the address itself: a redirect is not followed, and an answer longer than an address is refused; the default answers over IPv4 only, the family a machine's firewall is written for. A machine on the same network as Fadebox sees an internal address instead, and that one the provisioner reports without any of this — on Google, from the metadata server of the Compute Engine VM Fadebox runs on. Set it empty to disable the echo (an air-gapped installation has nothing to ask); Detect then offers only what the provisioner knows. |
FADEBOX_MACHINES_GCP_DEFAULT_IMAGE | projects/fadebox/global/images/family/fadebox-runtime | The boot image of every Google Compute Engine machine profile that names none. The default is the Fadebox runtime image, public to every authenticated Google account; an organisation that enforces constraints/compute.trustedImageProjects has to trust projects/fadebox first. Point it at your own image or family to move every such profile at once — an air-gapped installation, or one whose policy will not trust an outside project. A profile that names its own image still boots that one. Set it empty for no default: every profile then has to name its image, and one that does not is refused when it is saved. |
Log export
Caps for the instance log download, counted on raw bytes before zip compression. Docker cannot report a log's size in advance, so the export is scoped and capped rather than size-checked: a breached cap cuts that entry with a visible marker instead of failing the download.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_LOGS_EXPORT_DEFAULT_TAIL | 100000 | Lines per container when the request does not say — the recent end, which is the useful one. |
FADEBOX_LOGS_EXPORT_MAX_CONTAINER_BYTES | 64M | Ceiling for one container's entry. |
FADEBOX_LOGS_EXPORT_MAX_TOTAL_BYTES | 256M | Ceiling for the whole archive, allocated fairly across its containers. |
FADEBOX_LOGS_EXPORT_MAX_CONCURRENT | 2 | Simultaneous exports; each holds a worker thread and a daemon read for the whole download. Requests beyond it get a 429. |
The same scope and caps bound the log a stop or reset captures before removing the containers: one rule for how much of a log Fadebox will ever move, one truncation marker to recognise.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_LOGS_CAPTURE_DIR | ~/.fadebox/logs | Where captured logs are written, one directory per instance and deploy attempt, created owner-only. The bundled Compose file sets /data/logs, on the same volume as the git cache: state Fadebox owns and can lose. Wiping it loses the captures and nothing else — every reader then says there is no capture. Not worth backing up. |
FADEBOX_LOGS_CAPTURE_READ_TIMEOUT | PT2M | The most a capture may add to a stop, for all of its containers together. A runtime that stops answering mid-read costs that service its capture; the stop carries on. |
Git value sources
See Git value sources.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_GIT_CACHE_DIR | ~/.fadebox/git | Bare-clone cache making fetches incremental. Keep it on a volume — the shipped compose file does. It is an optimization only: an unreachable remote fails the deploy rather than serving stale content. |
FADEBOX_GIT_FETCH_TIMEOUT | PT30S | Ceiling on each remote operation. |
FADEBOX_GIT_ALLOWED_URL_SCHEMES | https | Schemes a repository URL may use. Deliberately narrow: this is the layer that actually opens the URL, and file:// would read the app container's own filesystem. |
Single sign-on
Providers themselves are database rows managed in the UI — see OIDC single sign-on. Only these two are environment variables.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_SECURITY_OIDC_REGISTRY_REFRESH_INTERVAL | 60s | How stale a provider edit made on another app instance may be here. A write on this instance applies immediately. Also the pickup interval for a row inserted straight into the database. |
FADEBOX_SECURITY_OIDC_ALLOWED_ISSUER_SCHEMES | https | Schemes an admin-supplied issuer URL may use. The server fetches the discovery document from it, so it is an outbound-request surface even though only a global admin can set one. |
:::info Removed variables
QUARKUS_OIDC_TENANT_ENABLED, QUARKUS_OIDC_AUTH_SERVER_URL, QUARKUS_OIDC_CLIENT_ID,
QUARKUS_OIDC_CREDENTIALS_SECRET, FADEBOX_SECURITY_OIDC_PROVIDER_NAME and
FADEBOX_SECURITY_OIDC_AUTO_PROVISION configured the old single-provider SSO. They no longer exist
— providers are managed at runtime instead. Delete them from your compose file.
:::
Audit log
Enterprise
Recording follows the license's Enterprise entitlement — there is deliberately no on/off switch here. See Audit log.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_AUDIT_LOG_MIRROR | false | Additionally emits every recorded event as one JSON line on the fadebox.audit logger, for shipping to a SIEM through the log pipeline. The database remains the source of truth. |
FADEBOX_AUDIT_RETENTION | (unset) | How long events are kept; unset keeps them forever. When set (ISO-8601, e.g. P365D), older events are purged — and the purge records its own audit.purged event on every tier, so the log never shrinks silently. Deliberately configuration-only, with no UI. |
FADEBOX_AUDIT_RETENTION_SWEEP_INTERVAL | PT1H | How often the retention purge runs, when retention is set. |
Licensing
Normally the license is pasted in Administration → License and stored in the database — see Licensing. The first two exist for infrastructure-as-code installs; a pasted license takes precedence over either.
| Variable | Default | What it does |
|---|---|---|
FADEBOX_LICENSE | (unset) | The license token itself. Unset means the Free tier. |
FADEBOX_LICENSE_PATH | (unset) | Path to a file holding the token (e.g. a compose secret). Setting both variables fails startup, as does a path that cannot be read. |
FADEBOX_LICENSE_STORE_URL | https://store.fadebox.io | Where the License page's Upgrade to Team and Upgrade to Enterprise links point. The links are composed in the browser and opened only when clicked; the server never calls the store. Leave it alone unless support tells you otherwise. |
Secrets at rest
Registry passwords, git repository tokens, runtime mTLS client keys, ACME DNS credentials and OIDC
client secrets are write-only against the API — accepted on save, never returned — and
encrypted in the database (AES-256-GCM) under FADEBOX_ENCRYPTION_KEY, so a database dump or
backup does not leak them. User passwords and API keys are hashed, not encrypted.
Two things this does not change: the key lives on the app host, so a full compromise of that host still yields the credentials — the encryption protects the database, its backups, and SQL-level access, not the host itself. And losing the key means losing the stored credentials: they cannot be read back out, only re-entered. Back the key up alongside the database.