Configuration
Fluxer reads its settings from environment variables. They live in a file named .env, in the same directory as docker-compose.yml.
For the bundled Compose stack, start with Core identity and public address and Secrets you must generate. The remaining sections cover custom settings and external services.
The installer in Get started writes .env for you and fills in every secret. Upgrading covers moving between releases.
How configuration is loaded
Section titled “How configuration is loaded”Compose forwards every setting a self-hosted instance can change from .env to each service that reads it. A few names keep a fixed value or are not passed at all, and Keys Compose does not forward lists them with the reason. Setting one of those in .env has no effect, so add it through a local Compose override.
Compose adds a default of its own only where the stack has to differ from the built-in default, and the entry below names it. Every other forwarded name reaches its service empty when .env leaves it out, and the service applies its built-in default.
Single-quote values containing a literal $ so Compose does not expand them as variable references:
POSTGRES_PASSWORD='ab$cd'A container’s environment is fixed when it is created. Apply changes with docker compose up -d, which recreates affected services. docker compose restart keeps the old environment.
Precedence, highest first:
- A value stored by the admin dashboard, for the settings listed under Runtime settings.
- The environment variable.
- The built-in default.
Use true or false for booleans, decimal integers for integer settings, and the specified object or array for JSON settings. Defaults and accepted values are listed below. Every service built from this version of the stack files or later reads an empty or blank value the same as an unset one, so an empty value restores the default. Older images do not, which Match the images to the stack files covers.
Reading a secret from a file
Section titled “Reading a secret from a file”Any setting a Fluxer service reads for its configuration can come from a file instead, such as a Docker secret. Set the variable’s name with _FILE appended to the file’s path, and leave the variable itself empty. The service reads the file at startup and drops one trailing newline. Startup fails, naming both variables, when the variable and its _FILE form are both set. It also fails, naming the path, when the file is missing, unreadable or not valid UTF-8. The name to use is the one the service sees, so POSTGRES_PASSWORD in .env becomes FLUXER_POSTGRES_PASSWORD_FILE.
Add the secrets through a local Compose override. FLUXER_SUDO_MODE_SECRET is read by api and worker only:
secrets: sudo_mode_secret: file: ./secrets/sudo_mode_secret
services: api: secrets: [sudo_mode_secret] environment: FLUXER_SUDO_MODE_SECRET: '' FLUXER_SUDO_MODE_SECRET_FILE: /run/secrets/sudo_mode_secret worker: secrets: [sudo_mode_secret] environment: FLUXER_SUDO_MODE_SECRET: '' FLUXER_SUDO_MODE_SECRET_FILE: /run/secrets/sudo_mode_secretCompose still checks the required names in .env before it applies the override, so keep a placeholder value there. Every Fluxer service gets the same shared settings, so a secret several services read, such as FLUXER_POSTGRES_PASSWORD, needs the same two entries on each of them: api, worker, gateway, media-proxy, push, admin, and snowflakes, users, gifs, messages and unfurl with their -shard services. Any service left out keeps the placeholder. The secret file must be readable by the user the container runs as.
The bundled backing containers read their own settings. The postgres container takes POSTGRES_PASSWORD: '' and POSTGRES_PASSWORD_FILE in its own environment, and on a fresh volume it creates the database with that password. seaweedfs-init, meilisearch and livekit have no _FILE form, so a file-based S3, Meilisearch or LiveKit secret still needs its real value in .env for them. FLUXER_ENV, LOG_LEVEL, FLUXER_DISABLE_RATE_LIMITS and the FLUXER_ERLANG_* settings other than FLUXER_ERLANG_COOKIE are read directly, so set them as plain values.
Core identity and public address
Section titled “Core identity and public address”FLUXER_DOMAIN is required. Everything else here is optional.
| Variable | Value in .env.example | Controls |
|---|---|---|
| FLUXER_DOMAIN | chat.example.com | The hostname users type. Reaches services as FLUXER_BASE_DOMAIN. Compose refuses to start when it is unset or empty |
| FLUXER_PUBLIC_SCHEME | https | The scheme users see. Must be http or https. Anything else fails startup |
| FLUXER_PUBLIC_PORT | 443 | The port users see. Integer. Omitted from derived URLs when it is the default for the scheme |
Outside Compose these fall back to an empty base domain, http, and port 8088. Compose supplies https and 443.
The API and worker require hostname-only domain settings: no scheme, port, credentials, path, query, fragment, or whitespace. Bracket IPv6 addresses. Valid hostname spelling is preserved, including case and a terminal root dot.
FLUXER_DOMAIN also feeds the default passkey relying party identifier, the default VAPID contact address, and the edge listener address.
FLUXER_PUBLIC_ORIGIN states the same public address as one string, and The public origin has it.
Secrets you must generate
Section titled “Secrets you must generate”Every value below ships as CHANGE_ME. Replace them all before the first start. Compose forwards each one from .env, and refuses to start when one is unset or empty, and names the variable. Generate each with openssl rand -hex 32, except the relay secret, the attachment URL secrets, and the VAPID pair, whose own entries name the command. The installer in Get started generates them all, so come here to rotate one value later or to set them up by hand.
POSTGRES_PASSWORD
Section titled “POSTGRES_PASSWORD”The database login, reused as FLUXER_POSTGRES_PASSWORD. Requires changing the stored role password too.
MEILI_MASTER_KEY
Section titled “MEILI_MASTER_KEY”The Meilisearch master key, reused as FLUXER_SEARCH_API_KEY. Recreate meilisearch and every service that searches.
FLUXER_S3_SECRET_KEY
Section titled “FLUXER_S3_SECRET_KEY”The object-storage secret, reused as FLUXER_S3_SECRET_ACCESS_KEY. seaweedfs-init installs it as the object store’s only S3 identity on every compose up, so a changed value takes effect on the next start.
FLUXER_SUDO_MODE_SECRET
Section titled “FLUXER_SUDO_MODE_SECRET”Sudo mode JWTs, as a raw HS256 key. Changing it ends every active sudo mode session.
FLUXER_CONNECTION_INITIATION_SECRET
Section titled “FLUXER_CONNECTION_INITIATION_SECRET”Connection initiation tokens and harvest download links. Changing it invalidates connection initiation tokens not yet verified and harvest download links already issued.
FLUXER_PROFILE_PSEUDONYM_SECRET
Section titled “FLUXER_PROFILE_PSEUDONYM_SECRET”Derives the stable placeholder name and tag that other users see while an account’s profile is hidden. Must be byte-identical on api, worker, and users-shard, or the same account shows two different names. Changing it gives every hidden profile a new placeholder. api and worker refuse to start without it when FLUXER_ENV is production. users-shard refuses to start without it unless FLUXER_ENV is development or test.
FLUXER_GATEWAY_RPC_AUTH_TOKEN
Section titled “FLUXER_GATEWAY_RPC_AUTH_TOKEN”Internal RPC between the API and the Gateway. Must be byte-identical on api, worker, and gateway.
FLUXER_MEDIA_PROXY_SECRET_KEY
Section titled “FLUXER_MEDIA_PROXY_SECRET_KEY”Media Proxy URLs. Must match across api, worker, media-proxy, gifs, and unfurl.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64”Generate with openssl rand -base64 32. Upload relay capability tokens. Must be standard base64 decoding to at least 32 bytes. Must match on api, worker, and media-proxy. api and worker refuse to start without it in every mode. media-proxy refuses to start without it when FLUXER_MEDIA_PROXY_MODE is upload or relay.
FLUXER_MEDIA_PROXY_ATTACHMENT_URL_SECRETS_BASE64
Section titled “FLUXER_MEDIA_PROXY_ATTACHMENT_URL_SECRETS_BASE64”Generate each entry with openssl rand -base64 32. Signs the attachment URLs Fluxer returns. A comma-separated list, where each entry must be standard base64 decoding to at least 32 bytes. Spaces around an entry and empty entries are ignored. The first entry signs and every entry verifies. Must match on api, worker, and media-proxy, and must differ from FLUXER_MEDIA_PROXY_SECRET_KEY. api and worker refuse to start when an entry is invalid, and with no entry they return unsigned URLs. media-proxy reads it only when FLUXER_MEDIA_PROXY_MODE is mp or upload, and then refuses to start with an invalid entry, or with no entry when FLUXER_MEDIA_PROXY_ATTACHMENT_SIGNATURE_MODE is report or enforce.
Compose forwards it from .env and passes it empty when unset, so a self-hosted instance returns unsigned URLs until an operator sets one. Setting it alone changes nothing on the read side, because FLUXER_MEDIA_PROXY_ATTACHMENT_SIGNATURE_MODE stays off until it is set too.
Rotate it in three steps, and apply each one with docker compose up -d before starting the next:
- Add the new secret as the second entry. Every
media-proxythen accepts it before any service signs with it. - Move the new secret to the first entry, so it signs.
- Remove the old secret no sooner than one day after step 2, because an ordinary URL it signed stays valid for up to a day.
A data package URL inside a harvest export never expires, so removing a secret ends every data package URL it signed at once, including the links inside exports already delivered, and those exports have to be rebuilt. Step 3 is safe for ordinary URLs alone.
FLUXER_ADMIN_SECRET_KEY_BASE
Section titled “FLUXER_ADMIN_SECRET_KEY_BASE”Admin sessions, CSRF tokens, and OAuth state. Changing it signs every admin out. The admin service refuses to start when it is empty.
FLUXER_ADMIN_OAUTH_CLIENT_SECRET
Section titled “FLUXER_ADMIN_OAUTH_CLIENT_SECRET”The admin OAuth2 client secret. The API requires a non-empty value to serve the admin application.
FLUXER_ERLANG_COOKIE
Section titled “FLUXER_ERLANG_COOKIE”The BEAM distribution secret. Only the Gateway reads it. Rotate it on every Gateway node at once when clustering.
LIVEKIT_API_SECRET
Section titled “LIVEKIT_API_SECRET”LiveKit access tokens. Must be at least 32 characters.
FLUXER_VAPID_PUBLIC_KEY
Section titled “FLUXER_VAPID_PUBLIC_KEY”Base64url of the 65-byte uncompressed P-256 point, unpadded, which is 87 characters. Config validation rejects any other shape at boot. install.sh derives it with openssl ecparam and openssl ec, and npx web-push generate-vapid-keys produces the same pair.
FLUXER_VAPID_PRIVATE_KEY
Section titled “FLUXER_VAPID_PRIVATE_KEY”The matching half of the pair. Base64url of the 32-byte P-256 scalar, unpadded, which is 43 characters. The API refuses to start when the scalar does not derive the public point. Both values are required even when nobody uses browser notifications.
The values below ship with a usable value. Both are required.
FLUXER_S3_ACCESS_KEY
Section titled “FLUXER_S3_ACCESS_KEY”.env.example fluxer. The object-storage access key. Reaches services as FLUXER_S3_ACCESS_KEY_ID, which config validation requires to be non-empty.
LIVEKIT_API_KEY
Section titled “LIVEKIT_API_KEY”.env.example fluxer. The LiveKit API key. Compose passes it to LiveKit as LIVEKIT_KEYS and as the webhook signing key, and to the API as FLUXER_LIVEKIT_API_KEY, so one change in .env moves them all.
Endpoint derivation from FLUXER_BASE_DOMAIN
Section titled “Endpoint derivation from FLUXER_BASE_DOMAIN”Fluxer builds its public endpoints from the scheme, the base domain, and the port. A port of 443 under https or wss, or 80 under http or ws, is omitted.
| Endpoint | Derived value |
|---|---|
| api, api_client | scheme, base domain, optional port, /api |
| app | scheme, base domain, optional port |
| gateway | ws or wss, base domain, optional port, /gateway |
| media | scheme, base domain, optional port, /media |
| static_cdn | scheme, base domain, optional port. With FLUXER_STATIC_CDN_DOMAIN set it becomes https:// and that domain, with no port |
| admin | scheme, base domain, optional port, /admin |
| marketing | scheme, base domain, optional port, /marketing |
| invite | scheme, FLUXER_INVITE_DOMAIN or the base domain, optional port, /invite |
| gift | scheme, FLUXER_GIFT_DOMAIN or the base domain, optional port, /gift |
Nothing in the stack reads X-Forwarded-Proto or X-Forwarded-Host. Every absolute URL comes from configuration, so set the scheme, the host, and the port by hand and keep them in sync with whatever terminates TLS.
The public origin
Section titled “The public origin”FLUXER_PUBLIC_ORIGIN states the public address as one string, with no trailing slash: the scheme, the host, and the port when that port is not the default for the scheme. FLUXER_PUBLIC_SCHEME, FLUXER_DOMAIN and FLUXER_PUBLIC_PORT state the same address between them, so the spellings have to agree.
Use an explicit http:// or https:// origin. Surrounding spaces and a single trailing slash are accepted. Credentials, paths, query strings, fragments, control characters, and empty or zero ports are rejected.
Leave it unset unless you need to state the full address explicitly. Compose otherwise derives the public URLs from the scheme, domain and port, including non-default ports.
If you set it, keep these settings consistent with it. A mismatch can break sign-in, setup, passkeys and media access.
Endpoint overrides
Section titled “Endpoint overrides”Each of these replaces its derived endpoint wholesale. Set one only when part of the instance answers at an address the derivation does not produce. All are optional.
The API and worker validate endpoint URLs at startup. The Gateway endpoint requires ws:// or wss://. The others require http:// or https://. Path prefixes and explicit ports are allowed. Credentials, fragments, whitespace, and backslashes are rejected. HTTP endpoint bases cannot contain a query. The Gateway URL may retain query parameters.
FLUXER_STATIC_CDN_DOMAIN
Section titled “FLUXER_STATIC_CDN_DOMAIN”Default empty. A separate host for static assets. When set, the static endpoint of api and worker is forced to https with no port. Only api and worker read it, so set FLUXER_STATIC_CDN_ENDPOINT to the same origin as well for admin, app-proxy and gateway. Compose forwards it from .env.
FLUXER_INVITE_DOMAIN
Section titled “FLUXER_INVITE_DOMAIN”Default empty. The host used in invite links. A hostname with no scheme and no path. Compose forwards it from .env.
FLUXER_GIFT_DOMAIN
Section titled “FLUXER_GIFT_DOMAIN”Default empty. The host used in gift links. A hostname with no scheme and no path. Compose forwards it from .env.
FLUXER_API_ENDPOINT
Section titled “FLUXER_API_ENDPOINT”Defaults to the derived endpoint. The public API base. Compose forwards it from .env to the services on the shared block, which leaves out app-proxy. The admin service reads the same name as its internal API target, so Compose sets it to http://api:8080 for that container.
FLUXER_API_CLIENT_ENDPOINT
Section titled “FLUXER_API_CLIENT_ENDPOINT”Defaults to the derived endpoint. The API base handed to clients. Setting only one of the API endpoints splits what clients see from what the instance advertises. Compose forwards it from .env.
FLUXER_APP_ENDPOINT
Section titled “FLUXER_APP_ENDPOINT”Defaults to the derived endpoint. The web app origin. Also one of the allowed CORS origins. Serving the client from another hostname requires setting this. Compose forwards it from .env, and builds it from the public origin when it is unset.
FLUXER_APP_ORIGIN_ALIASES
Section titled “FLUXER_APP_ORIGIN_ALIASES”Default empty. Further web app origins, comma separated. Each entry must be an HTTP or HTTPS origin, or the API refuses to start. The API treats each one like the FLUXER_APP_ENDPOINT origin. It allows it through CORS, reads invite links on its host, skips unfurling client routes on its host, and refuses webhook token calls from it. Set it when one instance serves the web app on more than one hostname. Compose forwards it from .env.
FLUXER_GATEWAY_ENDPOINT
Section titled “FLUXER_GATEWAY_ENDPOINT”Defaults to the derived endpoint. The public Gateway WebSocket URL. Compose forwards it from .env.
FLUXER_MEDIA_ENDPOINT
Section titled “FLUXER_MEDIA_ENDPOINT”Defaults to the derived endpoint. The public Media Proxy URL. The gifs service accepts it as a fallback for FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT. Compose forwards it from .env, and builds it from the public origin and /media when it is unset.
FLUXER_STATIC_CDN_ENDPOINT
Section titled “FLUXER_STATIC_CDN_ENDPOINT”Defaults to the derived endpoint. The static asset origin. Read by api, worker, admin, and app-proxy. Compose forwards it from .env. When it is unset, admin gets the public origin, and every other service derives it itself.
FLUXER_ADMIN_ENDPOINT
Section titled “FLUXER_ADMIN_ENDPOINT”Defaults to the derived endpoint. The admin origin. The API accepts exactly this value plus /oauth2_callback as the admin OAuth redirect URI. Compose forwards it from .env, and builds it from the public origin and FLUXER_ADMIN_BASE_PATH when it is unset.
The admin dashboard sets its cookie flags from the scheme of this value. Over HTTPS its CSRF cookie is named __Host-csrf_token and has Secure. Over HTTP it is named csrf_token without Secure.
FLUXER_DOCS_ENDPOINT
Section titled “FLUXER_DOCS_ENDPOINT”Defaults to an origin built from FLUXER_PUBLIC_SCHEME, FLUXER_BASE_DOMAIN and FLUXER_PUBLIC_PORT. The docs origin. Read by the docs server only, which the stack does not run, so Compose does not forward it.
FLUXER_MARKETING_ENDPOINT
Section titled “FLUXER_MARKETING_ENDPOINT”Defaults to the derived endpoint. The marketing origin. The second allowed CORS origin. Its hostname is extracted and matched. Compose forwards it from .env, and uses the public origin with no path when it is unset.
FLUXER_INVITE_ENDPOINT
Section titled “FLUXER_INVITE_ENDPOINT”Defaults to the derived endpoint. The invite base. The API reads links on its hostname as invite links and does not unfurl them. Compose forwards it from .env.
FLUXER_GIFT_ENDPOINT
Section titled “FLUXER_GIFT_ENDPOINT”Defaults to the derived endpoint. The gift base. The API does not unfurl links on its hostname. Compose forwards it from .env.
Internal endpoints address one container from another and never appear in a browser. FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT is required by gifs. The rest are optional.
FLUXER_INTERNAL_API_ENDPOINT
Section titled “FLUXER_INTERNAL_API_ENDPOINT”Default http://127.0.0.1:8080 in the Gateway. The API address used by gateway and push. push refuses to start without it. Compose sets http://api:8080.
FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT
Section titled “FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT”Default http://127.0.0.1:8082. The internal Media Proxy address for api and worker. FLUXER_MEDIA_PROXY_ENDPOINT is read as an alias when this name is unset or empty. Compose sets http://media-proxy:8080.
FLUXER_MEDIA_PROXY_ENDPOINT
Section titled “FLUXER_MEDIA_PROXY_ENDPOINT”No default. The internal Media Proxy address. unfurl-shard reads this name alone and exits without it. On api and worker it is an alias of FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT, read only when that name is unset or empty. Compose sets http://media-proxy:8080 on unfurl-shard only.
FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT
Section titled “FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT”No default. The public Media Proxy URL used by gifs, unfurl, and media-proxy. gifs requires this or FLUXER_MEDIA_ENDPOINT to start. Set it on media-proxy so requests for its own assets use local storage instead of an HTTP round trip. For api and worker, use FLUXER_MEDIA_ENDPOINT instead. Compose forwards it from .env to every app service. When it is unset, they get FLUXER_MEDIA_ENDPOINT, or the public media URL when that is unset too.
The edge
Section titled “The edge”The edge container is the only HTTP entry point. Neither layout below needs a change to the edge settings.
Bundled TLS is the default. docker compose up -d binds 80/tcp, 443/tcp, and 443/udp and obtains its own certificate for FLUXER_DOMAIN. Point DNS at the host and set nothing else. The 443 publishes stay on 443 whatever FLUXER_PUBLIC_PORT says, and FLUXER_HTTPS_PORT moves them.
Put your own reverse proxy in front by adding docker-compose.proxy.yml, a second Compose file that overrides parts of the first. Load it with -f twice, or set COMPOSE_FILE=docker-compose.yml:docker-compose.proxy.yml in .env once. The edge then serves plain HTTP on one port, and the proxy in front terminates TLS. Behind your own reverse proxy has the steps to enable the overlay, the requirements, and the per-proxy configuration.
All are optional.
FLUXER_EDGE_SITE_ADDRESS
Section titled “FLUXER_EDGE_SITE_ADDRESS”Defaults to the address Compose builds from FLUXER_PUBLIC_SCHEME and FLUXER_DOMAIN, with no port, so the edge listens on 443 or 80 in the container and FLUXER_HTTPS_PORT or FLUXER_HTTP_PORT maps it on the host. What the edge listens on, and the default keeps the listener on the address the instance advertises. Honoured in the bundled layout only, because docker-compose.proxy.yml sets the literal :8080 and tunnel.compose.yml the literal :80, and either discards any .env value. A site address that lists more than one hostname therefore needs the bundled layout. Write the scheme into any value you set here, because a bare hostname means automatic HTTPS on 443 whatever FLUXER_PUBLIC_SCHEME says. Compose forwards it from .env to edge.
FLUXER_EDGE_TRUSTED_PROXIES
Section titled “FLUXER_EDGE_TRUSTED_PROXIES”Default private_ranges. The peer addresses whose X-Forwarded-For header the edge trusts. The default is 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 127.0.0.1/8, fd00::/8 and ::1, which covers every same-host proxy. Narrow it to the proxy’s own address when the proxy reaches the instance from a public address, or when clients reach the proxy from a range the default already covers. Compose forwards it from .env to edge.
FLUXER_EDGE_ENCODE
Section titled “FLUXER_EDGE_ENCODE”Default zstd gzip. The response compression the edge offers, written as the arguments of the Caddy encode directive. gzip alone stops the edge offering zstd. Read by the edge container only, and it takes effect on docker compose up -d edge. Compose forwards it from .env to edge.
FLUXER_EDGE_BIND
Section titled “FLUXER_EDGE_BIND”Default 127.0.0.1:8080. Where the plain-HTTP port binds. Read only under the overlay. 0.0.0.0:8080 must be firewalled to the proxy host, because the edge takes the client address from the X-Forwarded-For header of any peer in FLUXER_EDGE_TRUSTED_PROXIES.
FLUXER_HTTP_PORT
Section titled “FLUXER_HTTP_PORT”Default 80. The host side of the edge’s HTTP publish, which serves the redirect to HTTPS and the ACME HTTP challenge under an https scheme and nothing at all under an http one. The container still listens on 80 inside. It takes an optional bind address in front of the port, so 127.0.0.1:80 keeps the publish off every public interface. Do not set it to the same host port as FLUXER_HTTPS_PORT, because that publishes one host port twice and the edge refuses to start. docker-compose.proxy.yml ignores it and publishes FLUXER_EDGE_BIND instead. tunnel.compose.yml reads it with a default of 127.0.0.1:80.
FLUXER_HTTPS_PORT
Section titled “FLUXER_HTTPS_PORT”Default 443. The host side of the edge’s HTTPS publish, and the only thing it moves is that host side. Set it when the host already has something on the port the instance advertises. It moves the TCP and the UDP publish together, because HTTP/3 needs both on the same port. It takes the same optional bind address in front of the port. The overlay does not read it.
COMPOSE_FILE
Section titled “COMPOSE_FILE”No default. Which Compose files are loaded. Read by Docker Compose itself. Set it to select the overlay for every command.
FLUXER_CADDY_SITE_ADDRESS is the old name for FLUXER_EDGE_SITE_ADDRESS. Compose reads it only when FLUXER_EDGE_SITE_ADDRESS is unset, so an older .env keeps the listener it already had. Rename FLUXER_CADDY_SITE_ADDRESS to FLUXER_EDGE_SITE_ADDRESS at any time.
Client IP
Section titled “Client IP”The edge resolves one client address per request and rewrites X-Forwarded-For to it on every upstream hop, so no service reads what a visitor sent. When the peer is outside FLUXER_EDGE_TRUSTED_PROXIES, the edge uses the peer address and discards the header. When the peer is inside the list, the edge takes the rightmost header entry that is not itself trusted. A proxy that appends to the header therefore still passes on the real client address. A client whose own address is inside the list resolves to the proxy, which is why Trusted proxies tells a LAN or VPN deployment to narrow it.
All are optional.
FLUXER_TRUST_CLIENT_IP_HEADER
Section titled “FLUXER_TRUST_CLIENT_IP_HEADER”Default false. Whether the client-IP header is trusted. Compose forwards it from .env with a default of true, because the edge sets the header on every upstream hop. The API has no peer-address fallback, so false on api makes every non-exempt request a 403.
FLUXER_CLIENT_IP_HEADER_NAME
Section titled “FLUXER_CLIENT_IP_HEADER_NAME”Default x-forwarded-for. Which header has the client IP. Read by api, worker, admin, and gateway. It must match what the edge sets. Compose forwards it from .env.
FLUXER_API_IP_BAN_EXEMPT_IPS
Section titled “FLUXER_API_IP_BAN_EXEMPT_IPS”Default empty. Addresses and CIDR ranges exempt from IP bans. Comma separated. A bare IPv4 address exempts that address, a bare IPv6 address exempts its /64, and a CIDR range such as 2001:db8:1200::/56 exempts every address in it. Every entry must parse as an IP or a CIDR range or the API fails at boot. Compose forwards it from .env.
The API returns 403 for any request whose client-IP header is missing, empty, or not a parsable address, and for every request while FLUXER_TRUST_CLIENT_IP_HEADER is false. The exceptions are /_health, /webhooks/livekit, /test, and the Bluesky client metadata and JWKS routes. The edge sets the header on every upstream hop, so a missing or unparsable header happens only in a layout that puts something other than the edge directly in front of api. A proxy in front of the edge that never sets the header passes the check, and every request then looks as though it came from the proxy.
FLUXER_API_TRUSTED_CALLERS
Section titled “FLUXER_API_TRUSTED_CALLERS”Default [], which keys every rate limit on the caller. A JSON array of trusted callers, such as a front end that talks to the API for its own visitors. Each entry is an object with a name, a key of at least 32 characters, and buckets, the rate limit buckets the caller may set the client address for. Only donation:request_link, donation:manage, donation:checkout, oauth:token and oauth:revoke accept it. Any other bucket name is ignored. A malformed entry or a short key fails the API at boot.
The caller sends its key in X-Fluxer-Internal-Key and its own client’s address in X-Fluxer-Client-Ip. The API keys the request on that address only when the key matches an entry that lists the route’s bucket. Otherwise it ignores both headers and keys the request on the caller, so a browser cannot set the address. Self-hosters leave this empty. Compose forwards it from .env.
[{"name": "bugs", "key": "<at least 32 characters>", "buckets": ["oauth:token", "oauth:revoke"]}]FLUXER_API_DONATION_PROXY_KEY
Section titled “FLUXER_API_DONATION_PROXY_KEY”Default empty. The older form of a trusted caller limited to the three donation buckets, for the marketing site. The key must be at least 32 characters or the API fails at boot. The API also accepts the address in the older X-Fluxer-Donor-Ip header. Self-hosters leave this empty. Compose forwards it from .env.
Images
Section titled “Images”These pick which container images Compose pulls. All are optional.
| Variable | Value in .env.example |
|---|---|
| FLUXER_REGISTRY_OWNER | fluxerapp |
| FLUXER_REGISTRY | ghcr.io/${FLUXER_REGISTRY_OWNER} |
| FLUXER_IMAGE_TAG | v1 |
FLUXER_REGISTRY_OWNER is the owner segment of the image names and falls back to fluxerapp. FLUXER_REGISTRY is the registry images are pulled from and falls back to ghcr.io/ followed by the owner. FLUXER_IMAGE_TAG is the tag on every Fluxer image and falls back to v1.
These affect only the Fluxer images. Each bundled third-party service has an image name of its own, which replaces the whole reference, registry and tag included. Set one to pull from a mirror or to move a service to another tag. All are optional.
FLUXER_CADDY_IMAGE: defaultcaddy:2.11-alpine, foredge.FLUXER_POSTGRES_IMAGE: defaultpostgres:16-alpine, forpostgres.FLUXER_VALKEY_IMAGE: defaultvalkey/valkey:9.1-alpine, forvalkey.FLUXER_NATS_IMAGE: defaultnats:2.14-alpine, fornats.FLUXER_MEILISEARCH_IMAGE: defaultgetmeili/meilisearch:v1.53, formeilisearch.FLUXER_SEAWEEDFS_IMAGE: defaultchrislusf/seaweedfs:4.47, forseaweedfsandseaweedfs-init.FLUXER_LIVEKIT_IMAGE: defaultlivekit/livekit-server:v1.12.0, forlivekit.
A new Postgres major does not read the data directory an older one wrote. Move FLUXER_POSTGRES_IMAGE to another major only through Move to a new Postgres major version. An upgrade refuses a refresh that would change the major the stack runs.
Database
Section titled “Database”FLUXER_POSTGRES_HOST, FLUXER_POSTGRES_DATABASE, FLUXER_POSTGRES_USERNAME, and FLUXER_POSTGRES_PASSWORD are required in production. The rest are optional.
FLUXER_DATABASE_BACKEND
Section titled “FLUXER_DATABASE_BACKEND”Default postgres. Which backend is used. Node accepts only postgres or cassandra. The internal services also accept postgresql, pg, scylla, and scylladb. Every service rejects any other value at startup. Compose sets postgres.
FLUXER_POSTGRES_URL
Section titled “FLUXER_POSTGRES_URL”Default empty. A full connection URL. When set, the discrete host, database, user, and password production checks are skipped. Compose forwards it from .env.
FLUXER_POSTGRES_HOST
Section titled “FLUXER_POSTGRES_HOST”Default 127.0.0.1. The database host. In production with postgres and no URL it must not be 127.0.0.1 or localhost. Compose forwards it from .env with a default of postgres, the bundled service.
FLUXER_POSTGRES_PORT
Section titled “FLUXER_POSTGRES_PORT”Default 5432. The database port. Integer 1 to 65535. Compose forwards it from .env.
FLUXER_POSTGRES_DATABASE
Section titled “FLUXER_POSTGRES_DATABASE”Default fluxer. The database name. Must be non-empty. Compose forwards it from .env, and passes the same value to the bundled postgres as POSTGRES_DB. The Postgres image creates that database only on its first start with an empty data directory, so set it before the first start. The installer’s backup takes it from the postgres container’s environment, as POSTGRES_DB, and skips the dump when FLUXER_POSTGRES_HOST or FLUXER_POSTGRES_URL points outside the stack.
FLUXER_POSTGRES_USERNAME
Section titled “FLUXER_POSTGRES_USERNAME”Default fluxer. The role. Must be non-empty. Compose forwards it from .env, and passes the same value to the bundled postgres as POSTGRES_USER, under the same first-start rule. The installer’s backup takes it from the postgres container’s environment, as POSTGRES_USER, and skips the dump when FLUXER_POSTGRES_HOST or FLUXER_POSTGRES_URL points outside the stack.
FLUXER_POSTGRES_PASSWORD
Section titled “FLUXER_POSTGRES_PASSWORD”Default fluxer. The password. The literal fluxer is rejected in production. CHANGE_ME is not. Compose fills it from POSTGRES_PASSWORD.
FLUXER_POSTGRES_SSL
Section titled “FLUXER_POSTGRES_SSL”Default false. TLS to the database. Must be true or false. When false, an sslmode other than prefer in FLUXER_POSTGRES_URL still applies. In production it must be true unless FLUXER_SELF_HOSTED=true. Compose forwards it from .env.
FLUXER_POSTGRES_SSL_CA
Section titled “FLUXER_POSTGRES_SSL_CA”Default empty. A CA certificate in PEM form. Used only when SSL is on. Compose forwards it from .env.
FLUXER_POSTGRES_MAX_CONNECTIONS
Section titled “FLUXER_POSTGRES_MAX_CONNECTIONS”Default 20. Pool size. Integer 1 to 1000, per process. The total is the sum across every container. Only api, worker, users-shard and messages-shard open a pool, and Compose sets it in each from the per-service name below.
FLUXER_API_POSTGRES_MAX_CONNECTIONS
Section titled “FLUXER_API_POSTGRES_MAX_CONNECTIONS”Default 25. The pool size of api. Read by Compose, which passes it to api as FLUXER_POSTGRES_MAX_CONNECTIONS.
FLUXER_WORKER_POSTGRES_MAX_CONNECTIONS
Section titled “FLUXER_WORKER_POSTGRES_MAX_CONNECTIONS”Default 25. The pool size of worker, passed the same way.
FLUXER_USERS_SHARD_POSTGRES_MAX_CONNECTIONS
Section titled “FLUXER_USERS_SHARD_POSTGRES_MAX_CONNECTIONS”Default 20. The pool size of users-shard, passed the same way.
FLUXER_MESSAGES_SHARD_POSTGRES_MAX_CONNECTIONS
Section titled “FLUXER_MESSAGES_SHARD_POSTGRES_MAX_CONNECTIONS”Default 20. The pool size of messages-shard, passed the same way.
FLUXER_POSTGRES_KV_TABLE
Section titled “FLUXER_POSTGRES_KV_TABLE”Default fluxer_kv. The key-value table name. Must match a safe Postgres identifier or startup fails. Compose forwards it from .env.
FLUXER_POSTGRES_PREPARED_STATEMENTS
Section titled “FLUXER_POSTGRES_PREPARED_STATEMENTS”Default true. Named prepared statements. Must be true or false. Compose forwards it in the shared block, so one value governs api, worker and the Rust services at once. Set it to false behind a transaction-pooling pooler such as PgBouncer, where a named statement outlives the session that declared it.
Cassandra or Scylla is an alternative backend, selected with FLUXER_DATABASE_BACKEND=cassandra. Supply a reachable database and configure the settings below when selecting it. The bundled stack uses Postgres, does not include a Cassandra or Scylla service, and forwards none of the names below.
FLUXER_CASSANDRA_HOSTS
Section titled “FLUXER_CASSANDRA_HOSTS”Default 127.0.0.1. Contact points. Comma separated. The Rust services normalise each entry to host and port, with IPv6 bracketed.
FLUXER_CASSANDRA_PORT
Section titled “FLUXER_CASSANDRA_PORT”Default 9042. The port. Integer.
FLUXER_CASSANDRA_KEYSPACE
Section titled “FLUXER_CASSANDRA_KEYSPACE”Default fluxer. The keyspace. Must exist already.
FLUXER_CASSANDRA_LOCAL_DC
Section titled “FLUXER_CASSANDRA_LOCAL_DC”Default datacenter1. The local datacentre. Read by api and worker only. The Rust services ignore it.
FLUXER_CASSANDRA_USERNAME
Section titled “FLUXER_CASSANDRA_USERNAME”Default empty. The login. Empty means no authentication.
FLUXER_CASSANDRA_PASSWORD
Section titled “FLUXER_CASSANDRA_PASSWORD”Default empty. The password. Empty means no authentication.
Cache and key-value
Section titled “Cache and key-value”The bundled stack requires Valkey for shared cache, live updates and deletion queues. General background jobs use NATS JetStream. Change these settings only when customising the cache or connecting an external Redis-compatible service.
FLUXER_KV_URL
Section titled “FLUXER_KV_URL”Default redis://localhost:6379/0. The Redis-compatible service address. Read by api and worker. Compose forwards it from .env with a default of redis://valkey:6379/0, the bundled service.
FLUXER_KV_MODE
Section titled “FLUXER_KV_MODE”Default standalone. Use standalone for a single Valkey server or cluster for a Redis-compatible cluster. Compose forwards it from .env.
FLUXER_SVC_CACHE_TTL_MS
Section titled “FLUXER_SVC_CACHE_TTL_MS”Default 30000. Cache lifetime in the internal services, in milliseconds. The users and messages routers ignore this setting because they keep no configurable response cache. Their shard caches still honour it. Compose forwards it from .env.
FLUXER_SVC_CACHE_MAX_ENTRIES
Section titled “FLUXER_SVC_CACHE_MAX_ENTRIES”Default 100000. Cache entry ceiling. Per process. The users and messages routers ignore this setting because they keep no configurable response cache. Their shard caches still honour it. Compose forwards it from .env.
FLUXER_GIFS_SHARD_CACHE_MAX_BYTES
Section titled “FLUXER_GIFS_SHARD_CACHE_MAX_BYTES”Default 536870912. GIF cache size. Must be at least 16777216. Set it to 134217728 to keep the cache inside the 256mb ceiling FLUXER_GIFS_SHARD_MEMORY_LIMIT gives gifs-shard. Compose forwards it from .env to gifs-shard.
FLUXER_IP_BAN_REFRESH_INTERVAL_MS
Section titled “FLUXER_IP_BAN_REFRESH_INTERVAL_MS”Default 300000. How often the API refreshes its IP-ban cache. A non-finite value or one at or below zero, such as 0, disables the timer. Compose forwards it from .env.
Keep persistent storage and the bundled noeviction policy to protect deletion queues and other shared state. Back up valkey-data. See Volumes and buckets for recovery implications.
Object storage
Section titled “Object storage”Every uploaded file lands in an S3-compatible object store, which the stack provides with SeaweedFS. FLUXER_S3_ACCESS_KEY_ID and FLUXER_S3_SECRET_ACCESS_KEY are required. The rest are optional. Run a data store outside the stack has the overlay that stops the bundled SeaweedFS once the stack points at another store.
FLUXER_S3_ENDPOINT
Section titled “FLUXER_S3_ENDPOINT”Default http://localhost:3900. The S3 API address. media-proxy defaults to empty instead. Compose forwards it from .env with a default of http://seaweedfs:8333, the bundled store.
FLUXER_S3_PUBLIC_ENDPOINT
Section titled “FLUXER_S3_PUBLIC_ENDPOINT”No default. The host substituted into presigned URLs, which are the only place it appears. Compose forwards it from .env, and uses FLUXER_S3_ENDPOINT when it is unset, except on app-proxy, which gets only a value .env sets and adds it to its Content Security Policy. admin reads it to find the origin that report downloads are presigned against. See FLUXER_S3_BUCKET_REPORTS.
FLUXER_S3_FORCE_PATH_STYLE
Section titled “FLUXER_S3_FORCE_PATH_STYLE”Default false. Path-style addressing. media-proxy defaults to true instead. Compose forwards it from .env with a default of true, which the bundled store needs.
FLUXER_S3_REGION
Section titled “FLUXER_S3_REGION”Default local. The region string. media-proxy defaults to us-east-1. Compose forwards it from .env with a default of us-east-1, so every service signs for the same region.
FLUXER_S3_ACCESS_KEY_ID
Section titled “FLUXER_S3_ACCESS_KEY_ID”Default empty. The access key. Config validation requires it non-empty on api and worker. Optional for media-proxy. Compose fills it from FLUXER_S3_ACCESS_KEY.
FLUXER_S3_SECRET_ACCESS_KEY
Section titled “FLUXER_S3_SECRET_ACCESS_KEY”Default empty. The secret key. Config validation requires it non-empty on api and worker. Optional for media-proxy. Compose fills it from FLUXER_S3_SECRET_KEY.
FLUXER_S3_SESSION_TOKEN
Section titled “FLUXER_S3_SESSION_TOKEN”Default empty. A temporary session token. Read by media-proxy only. Compose forwards it from .env.
FLUXER_S3_BUCKET_CDN
Section titled “FLUXER_S3_BUCKET_CDN”Default fluxer. Processed assets. media-proxy defaults to cdn. Compose forwards it from .env with a default of fluxer, so every service and seaweedfs-init name the same bucket.
FLUXER_S3_BUCKET_UPLOADS
Section titled “FLUXER_S3_BUCKET_UPLOADS”Default fluxer-uploads. Raw uploads. media-proxy defaults to uploads, app-proxy to fluxer-uploads. Compose forwards it from .env with a default of fluxer-uploads for the same reason.
FLUXER_S3_BUCKET_REPORTS
Section titled “FLUXER_S3_BUCKET_REPORTS”Default fluxer-reports. Abuse report evidence. Read by api, worker, and admin. Compose forwards it from .env with a default of fluxer-reports, so every service and seaweedfs-init name the same bucket. admin allows one image origin for report attachments and profile snapshots in its Content Security Policy, the origin api presigns report downloads against. It works that origin out from this name, FLUXER_S3_PUBLIC_ENDPOINT or else FLUXER_S3_ENDPOINT, FLUXER_S3_FORCE_PATH_STYLE and FLUXER_S3_BUCKET_UPLOADS, so give admin the same values as api. The images load only when that origin is an address browsers can reach.
FLUXER_S3_BUCKET_HARVESTS
Section titled “FLUXER_S3_BUCKET_HARVESTS”Default fluxer-harvests. Data archives. Read by api and worker. Compose forwards it from .env with a default of fluxer-harvests, so every service and seaweedfs-init name the same bucket.
FLUXER_S3_BUCKET_STATIC
Section titled “FLUXER_S3_BUCKET_STATIC”Default static. Static assets. Read by media-proxy only in static mode, which the stack does not use. api and worker never read it, and seaweedfs-init does not create this bucket. Compose does not forward it.
The Media Proxy read path has overrides of its own. All are optional.
FLUXER_S3_READ_ENDPOINT
Section titled “FLUXER_S3_READ_ENDPOINT”Falls back to FLUXER_S3_ENDPOINT. The read-side S3 address. Must be http or https with a host, and have no credentials, query string, or fragment. Compose forwards it from .env to media-proxy.
FLUXER_S3_READ_BUCKET
Section titled “FLUXER_S3_READ_BUCKET”Falls back to FLUXER_S3_BUCKET_CDN. The read-side bucket. Read by media-proxy only. Compose forwards it from .env.
FLUXER_S3_READ_BUCKET_STYLE
Section titled “FLUXER_S3_READ_BUCKET_STYLE”Defaults to path when path style is on, else virtual. How the bucket appears in the URL. path, virtual, or root. Anything else is a startup error. Compose forwards it from .env to media-proxy.
FLUXER_S3_READ_SIGNED
Section titled “FLUXER_S3_READ_SIGNED”Default false. Whether read requests are signed. Read by media-proxy only. Compose forwards it from .env with a default of true, because seaweedfs-init installs an S3 identity and the store then refuses anonymous reads. Set false only for a public-read bucket.
Search
Section titled “Search”The bundled stack provides Meilisearch for message search. These settings select and connect the search service.
FLUXER_SEARCH_ENGINE
Section titled “FLUXER_SEARCH_ENGINE”Default elasticsearch. Which engine is used. elasticsearch or meilisearch. Compose forwards it from .env with a default of meilisearch, the bundled engine.
FLUXER_SEARCH_URL
Section titled “FLUXER_SEARCH_URL”Default http://127.0.0.1:9200. The engine address. Compose forwards it from .env with a default of http://meilisearch:7700.
FLUXER_SEARCH_API_KEY
Section titled “FLUXER_SEARCH_API_KEY”Default empty. The API key. Meilisearch uses it as the key. Elasticsearch uses it as an API key that takes precedence over the username and password. Compose fills it from MEILI_MASTER_KEY.
FLUXER_SEARCH_USERNAME
Section titled “FLUXER_SEARCH_USERNAME”Default empty. Basic auth user. Elasticsearch only. Ignored under Meilisearch. Compose forwards it from .env.
FLUXER_SEARCH_PASSWORD
Section titled “FLUXER_SEARCH_PASSWORD”Default empty. Basic auth password. Elasticsearch only. Compose forwards it from .env.
FLUXER_SEARCH_TLS_REJECT_UNAUTHORIZED
Section titled “FLUXER_SEARCH_TLS_REJECT_UNAUTHORIZED”Default true. Certificate verification. Elasticsearch only. Never passed to the Meilisearch client. Compose forwards it from .env.
Message bus and internal services
Section titled “Message bus and internal services”The bundled stack requires NATS for communication between services and JetStream for background jobs. Compose forwards the connection settings from .env, with the bundled service as the default. Change them when using an external NATS service, which must have JetStream enabled for the worker connection.
FLUXER_NATS_URL
Section titled “FLUXER_NATS_URL”Default nats://127.0.0.1:4222. The core NATS address for api, worker, and gateway. The Gateway defaults to nats://nats:4222 instead. Compose forwards it from .env with a default of nats://nats:4222.
FLUXER_NATS_CORE_URL
Section titled “FLUXER_NATS_CORE_URL”Alias of FLUXER_NATS_URL on api and worker, read only when that name is unset or empty. The core NATS address. Not read by the Gateway. Compose does not forward it, because it forwards FLUXER_NATS_URL.
FLUXER_NATS_JETSTREAM_URL
Section titled “FLUXER_NATS_JETSTREAM_URL”Default nats://127.0.0.1:4222. The JetStream address for api and worker. Compose forwards it from .env, and uses FLUXER_NATS_URL when it is unset.
FLUXER_NATS_AUTH_TOKEN
Section titled “FLUXER_NATS_AUTH_TOKEN”Default empty. NATS authentication. Read by api, worker, gateway, and the internal services. The shipped NATS runs without authentication, and Compose forwards this name to every container that connects to it.
FLUXER_SVC_NATS_URL
Section titled “FLUXER_SVC_NATS_URL”Default nats://127.0.0.1:4222. The NATS address for the internal services and push. A separate variable from FLUXER_NATS_URL. Compose forwards it from .env, and uses FLUXER_NATS_URL when it is unset.
FLUXER_GATEWAY_API_RPC_ENDPOINT
Section titled “FLUXER_GATEWAY_API_RPC_ENDPOINT”No default. Where the Gateway calls the API. Read by the Gateway, which derives it from FLUXER_INTERNAL_API_ENDPOINT when it is unset. Compose does not forward it.
Compose configures the internal services. Change the following settings only when customising their deployment.
FLUXER_SVC_NAME
Section titled “FLUXER_SVC_NAME”Default default. The metrics prefix. It also selects the built-in default of FLUXER_SVC_MAX_CONCURRENT_REQUESTS. Changing it does not rename NATS subjects. Compose sets it per service.
FLUXER_SVC_MODE
Section titled “FLUXER_SVC_MODE”Default router. Must be router or shard. Any other value prevents startup. Compose sets it per service.
FLUXER_SVC_SHARD_COUNT
Section titled “FLUXER_SVC_SHARD_COUNT”Default 1. How many shards exist. Compose fixes it at 1.
FLUXER_SVC_SHARD_ID
Section titled “FLUXER_SVC_SHARD_ID”Derived from the numeric suffix of POD_NAME, else 0. Which shard this process is. A shard ID at or above the shard count is a startup error. Compose sets 0 on every shard.
FLUXER_SVC_LISTEN_HOST
Section titled “FLUXER_SVC_LISTEN_HOST”Default 0.0.0.0. The bind address. Serves health and metrics only. Compose does not forward it.
FLUXER_SVC_PORT
Section titled “FLUXER_SVC_PORT”Default 8090. The health and metrics port. Not published. Compose does not forward it, because every internal service health check probes 8090.
FLUXER_SVC_MAX_CONCURRENT_REQUESTS
Section titled “FLUXER_SVC_MAX_CONCURRENT_REQUESTS”Defaults to 192 for messages, 320 for snowflakes, 64 otherwise. In-flight request ceiling. The built-in defaults key off FLUXER_SVC_NAME. Compose forwards it from .env to every internal service router and shard. Left unset, each keeps its built-in default. Once set, the one value replaces the default on all of them, so a value below 320 also lowers the snowflakes ceiling. A router holds a slot for the whole round trip to its shard. A request over the ceiling is rejected at once. The API request behind it fails with a 503, and the API logs shard rejected the request because it is at its concurrency limit.
POD_NAME
Section titled “POD_NAME”No default. The shard ordinal source and node identity. Also read by the Gateway, and by the API as the pod_name in its RPC timing records. Compose does not set it.
The API reaches the internal services over NATS. Compose forwards the client names and the tuning values below from .env. It does not forward FLUXER_SNOWFLAKE_SERVICE_SUBJECT, FLUXER_USERS_SERVICE_SUBJECT or FLUXER_GIF_SERVICE_SUBJECT, because each has to match the subject its internal service listens on.
Numeric settings require decimal safe integers. Surrounding whitespace is trimmed, and omitted or blank settings use the defaults. Explicit malformed or out-of-range values are rejected.
FLUXER_SNOWFLAKE_SERVICE_NATS_CLIENT_NAME,FLUXER_USERS_SERVICE_NATS_CLIENT_NAMEandFLUXER_GIF_SERVICE_NATS_CLIENT_NAME: the name the API gives each NATS connection, as NATS monitoring shows it. Each defaults to a name for its service.FLUXER_SNOWFLAKE_SERVICE_BATCH_SIZE: defaults to128. Accepts 1 to 512.FLUXER_SNOWFLAKE_SERVICE_LOW_WATERMARK: defaults to32, capped below the batch size. Accepts 0 through batch size minus one.FLUXER_SNOWFLAKE_SERVICE_MAX_BUFFER_AGE_MS: defaults to5000. Accepts 1 to 60000 milliseconds.FLUXER_SNOWFLAKE_SERVICE_REQUEST_TIMEOUT_MS: defaults to6000. Accepts 1 to 60000 milliseconds.FLUXER_USERS_SERVICE_TIMEOUT_MS: defaults to6000. Accepts 1 to 2147483647 milliseconds.FLUXER_USERS_SERVICE_INFLIGHT_MAX_ENTRIES: defaults to10000. Accepts 0 through the largest safe integer.FLUXER_GIF_SERVICE_TIMEOUT_MS: defaults to12000. Accepts 1 to 2147483647 milliseconds.FLUXER_GIF_SERVICE_REGISTER_SHARE_TIMEOUT_MS: defaults to3000. Accepts 1 to 2147483647 milliseconds.
A Snowflake low watermark of 0 disables background refill before the buffer is empty. A Users in-flight entry limit of 0 disables request coalescing. It is not a limit on concurrent requests.
Voice and LiveKit
Section titled “Voice and LiveKit”LiveKit is the media server for voice and video. FLUXER_LIVEKIT_API_KEY and FLUXER_LIVEKIT_API_SECRET are required for voice. The rest are optional.
FLUXER_LIVEKIT_ENABLED
Section titled “FLUXER_LIVEKIT_ENABLED”Default false. The master switch for all voice and video. Compose forwards it from .env with a default of true, because the stack ships LiveKit.
FLUXER_LIVEKIT_API_KEY
Section titled “FLUXER_LIVEKIT_API_KEY”Default empty. The LiveKit API key. Compose fills it from LIVEKIT_API_KEY.
FLUXER_LIVEKIT_API_SECRET
Section titled “FLUXER_LIVEKIT_API_SECRET”Default empty. The LiveKit secret. Compose fills it from LIVEKIT_API_SECRET.
FLUXER_LIVEKIT_URL
Section titled “FLUXER_LIVEKIT_URL”Default empty. The client-facing LiveKit URL. Set it only when LiveKit is served from another host. docker-compose.yml never passes it empty: it builds the value from FLUXER_PUBLIC_ORIGIN, or from FLUXER_PUBLIC_SCHEME, FLUXER_DOMAIN and FLUXER_PUBLIC_PORT when the origin is unset, followed by /livekit, and forwards a value .env sets. An empty value outside Compose has the API derive wss://host/livekit from the public API origin, or ws:// under http, with a non-default port kept.
FLUXER_LIVEKIT_USE_EXTERNAL_IP
Section titled “FLUXER_LIVEKIT_USE_EXTERNAL_IP”Default true. Whether LiveKit discovers the address browsers dial by asking a STUN server. A host that cannot reach one over UDP fails to start with could not resolve external IP: context deadline exceeded. Set it to false and give FLUXER_LIVEKIT_NODE_IP the address instead. Read only by the livekit service.
FLUXER_LIVEKIT_NODE_IP
Section titled “FLUXER_LIVEKIT_NODE_IP”Default empty. The address LiveKit publishes in its ICE candidates. Empty leaves the choice to the discovery above, and with that discovery off LiveKit falls back to the container’s own address, which no browser can reach. Set both together. docker compose logs livekit names the address in use as nodeIP on the starting line.
FLUXER_LIVEKIT_STUN_PRIMARY
Section titled “FLUXER_LIVEKIT_STUN_PRIMARY”Default stun.l.google.com:19302. The first STUN server the discovery asks. Point it at another server to keep the discovery without reaching Google.
FLUXER_LIVEKIT_STUN_SECONDARY
Section titled “FLUXER_LIVEKIT_STUN_SECONDARY”Default stun1.l.google.com:19302. The second STUN server, used the same way.
FLUXER_LIVEKIT_INTERNAL_URL
Section titled “FLUXER_LIVEKIT_INTERNAL_URL”Default empty. The server-side LiveKit control API. Compose forwards it from .env with a default of http://livekit:7880, the bundled service.
FLUXER_LIVEKIT_DEFAULT_REGION
Section titled “FLUXER_LIVEKIT_DEFAULT_REGION”No default. The default voice region. JSON with id, name, emoji, latitude, and longitude. Compose forwards it from .env, with a default region named Default at latitude and longitude 0.
FLUXER_LIVEKIT_LOG_LEVEL
Section titled “FLUXER_LIVEKIT_LOG_LEVEL”Default info. How much LiveKit logs. Read by Compose only, which writes it into the LiveKit configuration as log_level.
FLUXER_LIVEKIT_TCP_PORT
Section titled “FLUXER_LIVEKIT_TCP_PORT”Default 7881. The TCP media port. Read by Compose only. Compose publishes it on the host and passes it to LiveKit as rtc.tcp_port, so the port LiveKit advertises in ICE candidates is the one the host forwards.
FLUXER_LIVEKIT_UDP_PORT
Section titled “FLUXER_LIVEKIT_UDP_PORT”Default 7882. The UDP media port, published and passed to LiveKit as rtc.udp_port the same way.
LiveKit media does not go through the edge. Compose publishes both media ports directly, so both must stay open in the host firewall and be forwarded to the host when it sits behind NAT. Compose points LiveKit at the webhook target http://api:8080/webhooks/livekit and configures no TURN server. The API accepts requests on that route without user authentication and without a client-IP header. A client that cannot use UDP falls back to ICE-TCP on the TCP port.
Moving a media port is one line in .env followed by docker compose up -d livekit. Compose puts the same value on the host side of the mapping, on the container side, and on the rtc port LiveKit advertises in the ICE candidates it hands to clients.
To rotate the key pair, set both names in .env, then run docker compose up -d livekit api worker to apply the change everywhere.
Peer-to-peer calls
Section titled “Peer-to-peer calls”A peer-to-peer call sends media directly between its participants and not through LiveKit or any relay. It runs only while the voice_p2p experiment is enabled in the instance configuration. The value below names the STUN servers Fluxer hands to each participant. It is optional and Compose forwards it from .env. The stack ships no STUN server.
FLUXER_VOICE_P2P_STUN_URLS
Section titled “FLUXER_VOICE_P2P_STUN_URLS”Default empty. A comma-separated list of stun: URLs. Participants use them to discover their public addresses.
Unset, participants receive no ICE servers and connect only when they can already reach each other, as on one local network. Two participants that cannot reach each other directly fail to connect, and either one can switch the call to LiveKit. Participants in a peer-to-peer call can learn each other’s IP addresses.
The Caddyfile is a bind mount, so the edge reads the copy that sits on disk beside docker-compose.yml. Editing it takes docker compose restart edge, because docker compose up -d leaves a container alone when only a mounted file changed. An upgrade does that restart itself, which What the script does covers. A change to any LiveKit value in .env needs docker compose up -d, because restart reuses the existing container with its old environment.
Only an instance where members sign in with email sends mail. A username instance sends none whatever the values below hold, and needs none of them. Sign-in methods covers the difference.
Email is off by default, and the conditions below turn it on. The email switch must be on. It is the value saved in the admin dashboard, or FLUXER_EMAIL_ENABLED when the dashboard has no saved value. Also, the provider must be smtp with a complete SMTP configuration, meaning FLUXER_EMAIL_FROM_EMAIL, FLUXER_EMAIL_SMTP_HOST, FLUXER_EMAIL_SMTP_PORT, FLUXER_EMAIL_SMTP_USERNAME, and FLUXER_EMAIL_SMTP_PASSWORD are all non-empty. All are optional.
The same conditions turn on a DNS check at registration. The check runs when email is on by the rule above. A switch or SMTP value saved in the admin dashboard wins over the matching variable. The address domain has to publish an MX record, or an A or AAAA record as a fallback, and an address at a domain that publishes neither is answered That email domain cannot receive mail. however well formed it is. The first admin account is no exception, so an owner address at a .lan, .internal or home.arpa name needs email left off.
FLUXER_EMAIL_ENABLED
Section titled “FLUXER_EMAIL_ENABLED”.env.example false. The delivery switch. The admin dashboard value wins over this. Compose forwards it from .env.
FLUXER_EMAIL_PROVIDER
Section titled “FLUXER_EMAIL_PROVIDER”.env.example none. The transport. smtp or none. Anything else fails startup. Compose forwards it from .env.
FLUXER_EMAIL_FROM_EMAIL
Section titled “FLUXER_EMAIL_FROM_EMAIL”.env.example [email protected]. The sender address. Default empty. Compose forwards it from .env, and uses noreply@localhost when it is unset.
FLUXER_EMAIL_FROM_NAME
Section titled “FLUXER_EMAIL_FROM_NAME”.env.example empty. The sender name. Default empty. An empty value sends mail under the instance product name, which is the name saved in the admin dashboard or else FLUXER_APP_PRODUCT_NAME, and a rename in the dashboard applies to the next email. A set value always wins. Compose forwards it from .env.
FLUXER_EMAIL_REPLY_TO_EMAIL
Section titled “FLUXER_EMAIL_REPLY_TO_EMAIL”Default empty. The address replies go to, sent as the Reply-To header on every email. An empty value sends no Reply-To header. Must be a single bare address such as [email protected], or the API fails at boot. The admin dashboard has no field for it. Compose forwards it from .env, and .env.example lists it commented out.
FLUXER_EMAIL_APP_BASE_URL
Section titled “FLUXER_EMAIL_APP_BASE_URL”.env.example empty. The base URL used in links. Must be http or https with no username, password, query, or fragment, or the API fails at boot. Falls back to the app endpoint. Compose forwards it from .env.
FLUXER_EMAIL_SMTP_HOST
Section titled “FLUXER_EMAIL_SMTP_HOST”.env.example empty. The SMTP host. Setting any SMTP variable creates the whole SMTP block, which is absent by default. Compose forwards it from .env.
FLUXER_EMAIL_SMTP_PORT
Section titled “FLUXER_EMAIL_SMTP_PORT”.env.example 587. The SMTP port. Falls back to 587. Compose forwards it from .env.
FLUXER_EMAIL_SMTP_USERNAME
Section titled “FLUXER_EMAIL_SMTP_USERNAME”.env.example empty. The SMTP login. Part of the completeness check. Compose forwards it from .env.
FLUXER_EMAIL_SMTP_PASSWORD
Section titled “FLUXER_EMAIL_SMTP_PASSWORD”.env.example empty. The SMTP password. Part of the completeness check. Compose forwards it from .env.
FLUXER_EMAIL_SMTP_TLS_MODE
Section titled “FLUXER_EMAIL_SMTP_TLS_MODE”.env.example empty. How the SMTP connection is encrypted. Anything other than the four values below fails startup. Compose forwards it from .env.
| Value | Connection |
|---|---|
implicit | TLS from the first byte. Most providers serve this on port 465 |
starttls | A plain connection that must upgrade with STARTTLS before the login. Most providers serve this on port 587 |
opportunistic | A plain connection that upgrades with STARTTLS when the server offers it, and stays plain when it does not |
none | No TLS, even when the server offers STARTTLS. Only for a mail server on a network you trust |
An empty value picks the mode from the port: implicit on port 465 and starttls on every other port. A mode saved in the admin dashboard wins over this variable, and choosing Automatic there hands the choice back to it. The dashboard shows the mode outgoing email ends up with. Automatic in the setup wizard saves the mode the port calls for, so it never follows this variable or FLUXER_EMAIL_SMTP_SECURE.
FLUXER_EMAIL_SMTP_SECURE
Section titled “FLUXER_EMAIL_SMTP_SECURE”.env.example unset. The older switch, read only while FLUXER_EMAIL_SMTP_TLS_MODE is empty. true means implicit and false means opportunistic. Compose forwards it from .env, and .env.example lists it commented out. An older .env can hold FLUXER_EMAIL_SMTP_SECURE=true next to port 587, and that pair fails against a STARTTLS server with wrong version number. Remove the line or set FLUXER_EMAIL_SMTP_TLS_MODE.
The Save and test button in the admin dashboard, and Create SMTP test with an empty body, connect with the saved settings, the same ones outgoing email uses, and fail with the reason when outgoing email is turned off or incomplete. A send that fails is logged by api or worker as SMTP send failed with the host, port and TLS mode.
The API reads FLUXER_EMAIL_WEBHOOK_SECRET for inbound delivery webhooks. Compose forwards it from .env, and .env.example lists it commented out.
The API registers POST /webhooks/sweego on every deployment. The route authenticates the Standard Webhooks header triple webhook-id, webhook-timestamp, and webhook-signature against that secret, and answers 404 Email not enabled while email is off. The route sits outside the client-IP exempt list that covers /webhooks/livekit, so give the provider a delivery URL that goes through the edge, https://chat.example.com/api/webhooks/sweego.
Only api and worker build the mail service, so recreating those two is enough after a change.
CAPTCHA
Section titled “CAPTCHA”ALTCHA proof-of-work is on by default. Turn it off or tune it under Instance Config, Bot protection in the admin panel. No environment variables configure it.
Single sign-on and passkeys
Section titled “Single sign-on and passkeys”All are optional.
FLUXER_SSO_ALLOW_PRIVATE_ADDRESSES
Section titled “FLUXER_SSO_ALLOW_PRIVATE_ADDRESSES”Default false. Whether the SSO provider URL may resolve to a private address. Off by default as SSRF protection. Turn it on only for split-horizon DNS or a LAN identity provider. Compose forwards it from .env.
FLUXER_PASSKEY_RP_ID
Section titled “FLUXER_PASSKEY_RP_ID”Defaults to the host of FLUXER_PUBLIC_ORIGIN when that is set, and to FLUXER_BASE_DOMAIN otherwise. The WebAuthn relying party identifier. Changing it invalidates every passkey already registered. Compose forwards it from .env with a default of FLUXER_DOMAIN, so a public origin on another host leaves existing passkeys valid.
FLUXER_PASSKEY_RP_NAME
Section titled “FLUXER_PASSKEY_RP_NAME”Default Fluxer. The relying party name browsers display. Does not follow FLUXER_DOMAIN. Compose forwards it from .env.
FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS
Section titled “FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS”The complete accepted origin set, comma separated. Despite the name, an explicit value replaces the defaults and must list every origin clients use. Compose forwards it from .env with a default of the public web app origin, so a self-hosted instance accepts its own origin alone. When the variable is omitted or empty outside Compose, the API accepts the configured app origin alongside the built-in Fluxer web and Android origins. To accept only the app origin, list it alone.
Each web entry must be an HTTP(S) origin with no credentials, path beyond an optional final slash, query, or fragment. Web origins are normalised to browser form using the configured public port unless the entry supplies one. Android entries must use android:apk-key-hash: followed by the signing certificate’s SHA-256 fingerprint in canonical, unpadded base64url (43 characters). Invalid entries and control characters fail startup.
Bluesky connections
Section titled “Bluesky connections”Bluesky connections are off by default. Enable them in the admin dashboard’s Runtime Integrations panel and supply an ES256 private signing key with a unique key identifier. The public API must serve the client metadata and signing keys.
For environment-based configuration, use FLUXER_AUTH_BLUESKY_ENABLED, FLUXER_AUTH_BLUESKY_KEYS, and the optional FLUXER_AUTH_BLUESKY_CLIENT_NAME, FLUXER_AUTH_BLUESKY_CLIENT_URI, FLUXER_AUTH_BLUESKY_LOGO_URI, FLUXER_AUTH_BLUESKY_TOS_URI, and FLUXER_AUTH_BLUESKY_POLICY_URI settings. Compose forwards every one from .env.
Web push
Section titled “Web push”FLUXER_VAPID_PUBLIC_KEY and FLUXER_VAPID_PRIVATE_KEY are required. FLUXER_VAPID_EMAIL is optional.
| Variable | Value in .env.example | Controls |
|---|---|---|
| FLUXER_VAPID_PUBLIC_KEY | CHANGE_ME | The VAPID public key. Base64url of the 65-byte uncompressed P-256 point |
| FLUXER_VAPID_PRIVATE_KEY | CHANGE_ME | The VAPID private key. Base64url of the 32-byte scalar, and the matching half of the pair |
| FLUXER_VAPID_EMAIL | unset | The VAPID contact address. Compose forwards it from .env, with a default of admin@ followed by FLUXER_DOMAIN |
FLUXER_GATEWAY_PUSH_ENABLED is read by the Gateway alone and defaults to true. When it is false, the Gateway hands no message or clear notification to push. Compose forwards it from .env.
Mobile push
Section titled “Mobile push”push reads every name below. api also reads the APNs names, apart from FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT. Compose forwards the APNs names from .env to both, and FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT and the FCM names to push alone. All are optional.
FLUXER_PUSH_APNS_ENABLED
Section titled “FLUXER_PUSH_APNS_ENABLED”Default false. The APNs switch. Read by api and push. Compose forwards it from .env.
FLUXER_PUSH_APNS_TEAM_ID
Section titled “FLUXER_PUSH_APNS_TEAM_ID”No default. The Apple team. Paired with the key ID. Compose forwards it from .env.
FLUXER_PUSH_APNS_KEY_ID
Section titled “FLUXER_PUSH_APNS_KEY_ID”No default. The signing key ID. Paired with the team ID. Compose forwards it from .env.
FLUXER_PUSH_APNS_PRIVATE_KEY
Section titled “FLUXER_PUSH_APNS_PRIVATE_KEY”No default. The signing key in PEM form. Set exactly one of this and the path. Compose forwards it from .env.
FLUXER_PUSH_APNS_PRIVATE_KEY_PATH
Section titled “FLUXER_PUSH_APNS_PRIVATE_KEY_PATH”No default. A path to the signing key. Must be readable inside the container. Compose forwards it from .env.
FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT
Section titled “FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT”Default production. Which APNs environment is used. production or development. Read by push only. Compose forwards it from .env.
FLUXER_PUSH_APNS_APPS
Section titled “FLUXER_PUSH_APNS_APPS”Default []. Per-app APNs configuration. JSON array. An entry with no app_id fails startup. Compose forwards it from .env.
FLUXER_PUSH_FCM_ENABLED
Section titled “FLUXER_PUSH_FCM_ENABLED”Default false. The FCM switch. Read by push only, like every FCM name. Compose forwards it from .env.
FLUXER_PUSH_FCM_PROJECT_ID
Section titled “FLUXER_PUSH_FCM_PROJECT_ID”No default. The Firebase project. Paired with the client email. Compose forwards it from .env.
FLUXER_PUSH_FCM_CLIENT_EMAIL
Section titled “FLUXER_PUSH_FCM_CLIENT_EMAIL”No default. The service account address. Paired with the project. Compose forwards it from .env.
FLUXER_PUSH_FCM_PRIVATE_KEY
Section titled “FLUXER_PUSH_FCM_PRIVATE_KEY”No default. The service account key. Set one of this, FLUXER_PUSH_FCM_PRIVATE_KEY_PATH, or FLUXER_PUSH_FCM_SERVICE_ACCOUNT_JSON_PATH. Compose forwards it from .env.
FLUXER_PUSH_FCM_PRIVATE_KEY_PATH
Section titled “FLUXER_PUSH_FCM_PRIVATE_KEY_PATH”No default. A path to the key. Must be readable inside the container. Compose forwards it from .env.
FLUXER_PUSH_FCM_SERVICE_ACCOUNT_JSON_PATH
Section titled “FLUXER_PUSH_FCM_SERVICE_ACCOUNT_JSON_PATH”No default. A path to the whole service account JSON. Must be readable inside the container. Compose forwards it from .env.
FLUXER_PUSH_FCM_TOKEN_URI
Section titled “FLUXER_PUSH_FCM_TOKEN_URI”Default https://oauth2.googleapis.com/token. The OAuth token endpoint. Change only for a proxy or a test double. Compose forwards it from .env.
FLUXER_PUSH_FCM_APPS
Section titled “FLUXER_PUSH_FCM_APPS”Default []. Per-app FCM configuration. JSON array, under the same app_id rule as APNs. Compose forwards it from .env.
Push service settings
Section titled “Push service settings”push is the push notification service in the bundled stack. It reads the VAPID pair from Web push, the APNs and FCM names from Mobile push, and FLUXER_SVC_NATS_URL with FLUXER_NATS_AUTH_TOKEN from Message bus and internal services. It refuses to start without FLUXER_INTERNAL_API_ENDPOINT and FLUXER_GATEWAY_RPC_AUTH_TOKEN. Compose supplies both. The names below belong to push, and Compose forwards every one from .env except the bind address and port. All are optional.
FLUXER_PUSH_SERVICE_HOST
Section titled “FLUXER_PUSH_SERVICE_HOST”Default 0.0.0.0. The bind address. It must parse as an IP address. Also settable with --bind-host. Compose does not set it, so the default applies.
FLUXER_PUSH_SERVICE_PORT
Section titled “FLUXER_PUSH_SERVICE_PORT”Default 8126. The listen port for the health and metrics endpoints. Also settable with --port. Compose does not set it, so the default applies.
FLUXER_PUSH_SERVICE_QUEUE_CAPACITY
Section titled “FLUXER_PUSH_SERVICE_QUEUE_CAPACITY”Default 10000. Notification jobs push holds at once. Accepts 1 to 1000000. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_SEND_CONCURRENCY
Section titled “FLUXER_PUSH_SERVICE_SEND_CONCURRENCY”Default 256. Provider requests in flight at once, across every job. Accepts 1 to 65536. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_APNS_BASE_URL
Section titled “FLUXER_PUSH_SERVICE_APNS_BASE_URL”No default. Replaces the APNs host in both environments. Set it only for a test double. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_FCM_BASE_URL
Section titled “FLUXER_PUSH_SERVICE_FCM_BASE_URL”Default https://fcm.googleapis.com. The FCM host. Set it only for a proxy or a test double. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_RELAY_CONSENT_ACCEPTED
Section titled “FLUXER_PUSH_SERVICE_RELAY_CONSENT_ACCEPTED”Default false. Accepts the push relay supplemental privacy notice for this push process. true, 1, or yes accepts it, and false, 0, or no leaves the decision to the push relay configuration. Any other value fails startup. When it is true, push sends through the Fluxer-run relay even while the stored consent is off. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_MANAGED_RELAY_HOSTS
Section titled “FLUXER_PUSH_SERVICE_MANAGED_RELAY_HOSTS”Default push.fluxer.com. Hostnames, comma separated, of the Fluxer-run push relay. A browser push endpoint on one of them needs the relay consent above, and a refusal from it counts against the relay quota. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_OWN_RELAY_HOSTS
Section titled “FLUXER_PUSH_SERVICE_OWN_RELAY_HOSTS”Default empty. Hostnames, comma separated, of a push relay this instance runs itself. push delivers a browser push endpoint on one of them straight through its own APNs or FCM credentials, with no HTTP hop. Compose forwards it from .env.
FLUXER_PUSH_SERVICE_PRIVATE_HOSTS
Section titled “FLUXER_PUSH_SERVICE_PRIVATE_HOSTS”Default empty. Hostnames, comma separated, that a push endpoint may use even when they resolve to private, loopback, or other non-public addresses, such as a ntfy server on your own network. push refuses every other such endpoint and logs blocked_address. Compose forwards it from .env.
push also runs as a relay under --mode relay, which the stack does not use. Only that mode reads FLUXER_PUSH_RELAY_MAX_CONCURRENT, default 1024, FLUXER_PUSH_RELAY_MAX_BODY_BYTES, default 2816, FLUXER_PUSH_RELAY_TRUSTED_PROXY_HOPS, default 1, and FLUXER_PUSH_RELAY_SOURCE_BUCKET_ENABLED, default false. Compose does not forward them, and Names the stack has no use for lists them.
Payments
Section titled “Payments”Stripe billing for a paid premium tier and gift purchases. All are optional. On a self-hosted instance, set billing in the admin dashboard’s Premium & billing group instead. A value stored there wins over the matching variable below.
A self-hosted instance sells premium only while its premium mode is mirror, billing is switched on, a Stripe secret key is set, and at least one currency has both a monthly and a yearly price. Instance discovery reports this as stripe_enabled. Existing subscribers can still manage, cancel and renew while the premium mode is mirror and a Stripe secret key is set, even after sales are switched off or the prices are removed. Instance discovery reports this as stripe_serviceable. Stripe sends events to https://<FLUXER_DOMAIN>/api/stripe/webhook, which the dashboard shows as the webhook URL to register in Stripe.
FLUXER_STRIPE_ENABLED
Section titled “FLUXER_STRIPE_ENABLED”Default false. The Stripe switch. Compose forwards it from .env.
FLUXER_STRIPE_SECRET_KEY
Section titled “FLUXER_STRIPE_SECRET_KEY”Default empty. The Stripe secret key. Compose forwards it from .env.
FLUXER_STRIPE_WEBHOOK_SECRET
Section titled “FLUXER_STRIPE_WEBHOOK_SECRET”Default empty. The webhook signing secret. Compose forwards it from .env.
FLUXER_STRIPE_PRICES
Section titled “FLUXER_STRIPE_PRICES”Default {}. Every price ID at once. JSON object. Compose forwards it from .env.
Individual price variables also exist, one per product and currency: FLUXER_STRIPE_PRICE_MONTHLY_, FLUXER_STRIPE_PRICE_YEARLY_, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_ and FLUXER_STRIPE_PRICE_GIFT_1_YEAR_ in USD, EUR, BRL, DKK, INR, ISK, NOK, PLN, SEK, and TRY. Each overrides the matching price in this object. Compose does not forward them, so a self-hosted instance sets prices with this variable or in the admin dashboard.
Prices saved in the dashboard replace this catalogue entirely. They take any three-letter currency code. A buyer gets the currency mapped to their country, then the default currency, then the first configured one. Fluxer’s country checks for localised currencies apply only to prices set by these variables.
A self-hosted instance never sets Stripe Adaptive Pricing on its Checkout Sessions, so the setting in the Stripe Dashboard applies. The hosted service turns it off for SEK, NOK, DKK and ISK sessions so buyers pay the native price.
FLUXER_STRIPE_LEGACY_PRICES
Section titled “FLUXER_STRIPE_LEGACY_PRICES”Default {}. Retired price IDs, keyed by the same slot names FLUXER_STRIPE_PRICES uses, each mapped to a list: {"monthly_brl": ["price_..."]}. JSON object. Compose forwards it from .env. Existing subscriptions on these prices keep renewing. The localised checkout catalogue uses FLUXER_STRIPE_PRICES alone. Legacy prices saved in the dashboard replace this variable, and there the currency in a slot name is upper case, such as monthly_GBP.
Repricing a slot has a fixed order. Doing the steps in another order leaves a price ID unknown to the API, and invoices on that price fail. Create the new price in Stripe, move the ID it replaces into this variable, roll the API, and only then point FLUXER_STRIPE_PRICES at the new price. A price ID that neither variable names is unknown to the API: a renewal invoice on it fails the webhook with Unknown product for invoice renewal, a checkout completing on it fails with Unknown price ID for checkout session, and both keep failing until the ID is registered. The API answers Stripe as soon as the signature verifies and hands the event to worker, so any retry comes from worker rerunning the processStripeWebhook job: the Stripe dashboard shows the delivery as succeeded and the error is in the worker logs. Keep a retired ID listed for as long as any subscription still bills on it, which for a yearly price is at least a year after the switch.
With prices saved in the dashboard, add the retired ID to its legacy prices before you replace the price.
Moderation and abuse
Section titled “Moderation and abuse”All are optional.
FLUXER_CLAMAV_ENABLED
Section titled “FLUXER_CLAMAV_ENABLED”Default false. Upload virus scanning. Compose forwards it from .env, and no ClamAV container ships.
FLUXER_CLAMAV_HOST
Section titled “FLUXER_CLAMAV_HOST”Default 127.0.0.1. The scanner host. Needs a reachable scanner. Compose forwards it from .env.
FLUXER_CLAMAV_PORT
Section titled “FLUXER_CLAMAV_PORT”Default 3310. The scanner port. Integer. Compose forwards it from .env.
FLUXER_CLAMAV_FAIL_OPEN
Section titled “FLUXER_CLAMAV_FAIL_OPEN”Default false. Behaviour when the scanner is unreachable. With scanning on and this off, an unreachable scanner rejects every upload. Compose forwards it from .env.
FLUXER_BLOCKLIST_FEEDS_ENABLED
Section titled “FLUXER_BLOCKLIST_FEEDS_ENABLED”Defaults to the inverse of FLUXER_SELF_HOSTED. Off by default on a self-hosted instance. With feeds on, the worker downloads the URLhaus and PhishTank URL lists every six hours, and MalwareBazaar file hashes every twelve hours. Fluxer checks posted links against the URLs and uploads against the hashes.
With feeds off, Fluxer checks none of this data. The first worker start with feeds off removes the URL feed file and every malware_bazaar file-SHA ban that no Admin added. File-SHA bans added through the Admin API stay. Turning feeds back on downloads the URLs within six hours and the hashes within twelve. Compose forwards it from .env.
FLUXER_REPORT_RETENTION_DAYS
Section titled “FLUXER_REPORT_RETENTION_DAYS”Default 365. Read by worker. The daily report retention job deletes each report this many days after it was filed, pending or resolved, with its evidence copies and search entry, unless a legal hold is in place. Integer 1 to 36500, checked at startup. Compose forwards it from .env.
FLUXER_RESOLVED_REPORT_RETENTION_DAYS
Section titled “FLUXER_RESOLVED_REPORT_RETENTION_DAYS”Default unset, which leaves resolved reports to FLUXER_REPORT_RETENTION_DAYS. Read by worker. When set, a resolved report is deleted this many days after it was resolved, if that comes before the end of its retention period. Pending reports are not affected, and a legal hold still keeps a report. Integer 1 to 36500, checked at startup. Compose forwards it from .env.
FLUXER_REPORT_RETENTION_DRY_RUN
Section titled “FLUXER_REPORT_RETENTION_DRY_RUN”Default false, so report retention deletes for real. Read by worker. With true, each run only counts what it would delete and deletes nothing. true or false, checked at startup. Use it to check the counts in the Processed report retention log line before the first real run, because deleted evidence cannot be restored. Compose forwards it from .env.
FLUXER_BREACHED_PASSWORD_CHECK_ENABLED
Section titled “FLUXER_BREACHED_PASSWORD_CHECK_ENABLED”Defaults to the inverse of FLUXER_SELF_HOSTED. Breached password rejection. Sends the first five characters of the password’s SHA-1 hash to api.pwnedpasswords.com. Off by default on a self-hosted instance. Compose forwards it from .env.
Stored instance policy
Section titled “Stored instance policy”Use the admin dashboard or Admin instance API to change saved settings. Missing settings use their documented defaults. Invalid JSON, field types, identifiers or out-of-range values cause an error instead of silently resetting security or registration policy.
Invalid saved configuration prevents the API and worker from starting. If a running process cannot apply an update, it logs the error and keeps its previous valid settings. Check the reported section and field paths, repair the saved configuration, then retry. It is not repaired automatically.
The API and worker read configuration saved by older releases under these rules:
- Gateway rollout accepts
nats_request_timeout_msonly whenrpc_request_timeout_msis absent. Both require an integer from 1000 to 60000. Use the current name in Admin requests. - Registration accepts
adminRegistrationUrlsEnabledonly whenadmin_registration_urls_enabledis absent. Missing registration settings default toopenwith admin registration URLs enabled. - Missing SSO settings default to disabled. A missing enforcement flag takes the value of the enabled flag, and automatic provisioning defaults to on. Stored flags require
trueorfalse. - SSO allowed domains accept a JSON string array or a legacy comma-separated list of at most 100 entries. Domains are trimmed, lowercased, IDNA encoded and deduplicated. An empty list leaves domains unrestricted. Invalid lists must be repaired even while SSO is disabled.
- A self-hosted instance with no stored sign-in method gets one on its next start, by the rules in Sign-in methods. Until that write succeeds the instance behaves as an email instance.
- Missing registration URL and pending-registration lists mean empty lists. Invalid records are rejected rather than discarded. Timestamps require an explicit UTC marker or offset, and null is accepted only for nullable fields.
Explicit false values and supported null clears remain valid. Branding and integration strings retain their documented trimming and blank-value behaviour. Media size and lifetime adjustments also remain unchanged. These checks do not make a multi-section update atomic. Read back the result after a failed write before retrying.
Limits
Section titled “Limits”No environment variable changes an instance limit. Use the admin dashboard’s Limit Config page or the Admin API. The thread and forum limits are fixed and have no limit key. Thread lifecycle, Thread members and Forum channel fields state them. Effective limits combine saved settings, deployment defaults and premium policy. Only the values you change are saved. Every other limit follows the deployment defaults and the premium model, so switching the model moves the untouched limits and keeps your edits. Reset to Defaults on a rule in the dashboard drops the edits saved for that rule. After a change is saved, every running api and worker process reloads the limits with no restart. Clients receive the limit configuration object.
Request concurrency is separate from instance limits, and each process sets its own. All are optional.
FLUXER_API_MAX_INFLIGHT_REQUESTS
Section titled “FLUXER_API_MAX_INFLIGHT_REQUESTS”Default 512. The API in-flight ceiling. Integer 1 to 100000, checked at startup. Compose forwards it from .env.
FLUXER_GATEWAY_HTTP_RPC_MAX_CONCURRENCY
Section titled “FLUXER_GATEWAY_HTTP_RPC_MAX_CONCURRENCY”Default 512. Gateway HTTP RPC concurrency. Compose forwards it from .env to gateway.
FLUXER_GATEWAY_NATS_RPC_MAX_HANDLERS
Section titled “FLUXER_GATEWAY_NATS_RPC_MAX_HANDLERS”Default 512. Gateway NATS handler count. Compose forwards it from .env to gateway.
FLUXER_DISABLE_RATE_LIMITS
Section titled “FLUXER_DISABLE_RATE_LIMITS”Default false. Turns rate limits off. Read by the API and by the Gateway. The Gateway turns rate limits off only for the values 1, true and TRUE. A test setting, which Compose does not forward.
FLUXER_RELAX_REGISTRATION_RATE_LIMITS
Section titled “FLUXER_RELAX_REGISTRATION_RATE_LIMITS”Default false. Loosens registration rate limits. A test setting, which Compose does not forward.
Logging and build metadata
Section titled “Logging and build metadata”All are optional.
LOG_LEVEL
Section titled “LOG_LEVEL”Defaults to debug in development, info otherwise. The Node log level. Read by api and worker. Compose forwards it from .env.
RUST_LOG
Section titled “RUST_LOG”Default info. The Rust log filter. Read by media-proxy, app-proxy, admin, push, and the internal services. An empty or invalid filter also gives info, and off silences them. Compose forwards it from .env.
FLUXER_GATEWAY_LOGGER_LEVEL
Section titled “FLUXER_GATEWAY_LOGGER_LEVEL”Default info. The Gateway log level. An unrecognised level also gives info. Compose forwards it from .env to gateway.
LOGGER_LEVEL
Section titled “LOGGER_LEVEL”Falls back to FLUXER_GATEWAY_LOGGER_LEVEL. The Gateway log level at runtime. Overrides the prefixed name, and an unrecognised level falls back to it. Compose forwards it from .env to gateway.
BUILD_VERSION
Section titled “BUILD_VERSION”Default dev. The reported build. Baked into the images, so Compose does not set it. When BUILD_VERSION is unset or empty outside development, the process prints a warning.
RELEASE_CHANNEL
Section titled “RELEASE_CHANNEL”Default stable. The reported channel. Only canary is recognised as non-stable. Read by app-proxy only. It describes the shipped build, so Compose does not set it.
HOSTNAME
Section titled “HOSTNAME”Set by Docker. Node identity in metrics and logs.
FLUXER_ENV
Section titled “FLUXER_ENV”Default development. The runtime mode. development, production, or test. Compose pins production, which gates Postgres validation and the admin cookie flags.
FLUXER_SELF_HOSTED
Section titled “FLUXER_SELF_HOSTED”Default false. The self-host switch. Compose sets true. It relaxes the production Postgres SSL requirement, seeds the limit tier, gates registration, donation and discovery controllers, keeps premium billing off until it is set up as Payments describes, turns blocklist feeds off, and keeps hosted-only artwork from fluxer.app out of the app, webhook avatars and link embeds.
/_metrics on api, media-proxy, gateway, and push, plus the Gateway’s /_health/ready, /_health/drain, and /_health/undrain, are gated to loopback peers, so no proxy reaches them. The probes that work from outside are /api/_health, /gateway/_health, /media/_health, and the edge’s own /_health.
Feature flags and development switches
Section titled “Feature flags and development switches”All are optional.
FLUXER_DISCOVERY_ENABLED
Section titled “FLUXER_DISCOVERY_ENABLED”Default true. The public guild discovery surface. With it off, discovery search, discovery join, and the guild discovery application routes return 400 DISCOVERY_DISABLED. Compose forwards it from .env.
FLUXER_DISCOVERY_MIN_MEMBER_COUNT
Section titled “FLUXER_DISCOVERY_MIN_MEMBER_COUNT”Default 1. Minimum members for discovery eligibility. Integer. Compose forwards it from .env.
FLUXER_DELETION_GRACE_PERIOD_HOURS
Section titled “FLUXER_DELETION_GRACE_PERIOD_HOURS”Default 336. How long a deleted account is recoverable. Forced to 0.01 hours when FLUXER_TEST_MODE_ENABLED is on. Compose forwards it from .env.
FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED
Section titled “FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED”Default false. Presigned attachment uploads. Only api reads it. Compose forwards it from .env to api with a default of true.
FLUXER_API_PRESIGNED_HARVEST_DOWNLOADS_ENABLED
Section titled “FLUXER_API_PRESIGNED_HARVEST_DOWNLOADS_ENABLED”Default true. Presigned harvest downloads. Applies to the harvests bucket. A presigned link points at FLUXER_S3_PUBLIC_ENDPOINT, which in the bundled stack is the internal http://seaweedfs:8333 that no browser reaches. Compose therefore forwards it from .env with a default of false. Turn it on once FLUXER_S3_PUBLIC_ENDPOINT names an address browsers can reach.
FLUXER_API_STORAGE_CHANGE_FEED_ENABLED
Section titled “FLUXER_API_STORAGE_CHANGE_FEED_ENABLED”Default false. Whether api and worker publish a record of every object-store write and delete to a JetStream stream. Compose forwards it from .env.
FLUXER_API_STORAGE_CHANGE_FEED_STREAM
Section titled “FLUXER_API_STORAGE_CHANGE_FEED_STREAM”Default STORAGE_CHANGES. The JetStream stream the feed publishes to. Letters, digits, underscores or hyphens, checked at startup only while the feed is on. Compose forwards it from .env.
FLUXER_API_STORAGE_CHANGE_FEED_SKIP_BUCKETS
Section titled “FLUXER_API_STORAGE_CHANGE_FEED_SKIP_BUCKETS”Defaults to the uploads bucket. Buckets the feed leaves out, comma separated. none leaves out no bucket. Compose forwards it from .env.
FLUXER_API_UNFURL_IGNORED_HOSTS
Section titled “FLUXER_API_UNFURL_IGNORED_HOSTS”Default empty. Hosts never unfurled. Comma separated, unvalidated. Compose forwards it from .env.
FLUXER_TEST_MODE_ENABLED
Section titled “FLUXER_TEST_MODE_ENABLED”Default false. Test mode. Collapses the deletion grace period. A test setting, which Compose does not forward.
FLUXER_TEST_HARNESS_TOKEN
Section titled “FLUXER_TEST_HARNESS_TOKEN”No default. The test harness credential. A test setting, which Compose does not forward.
FLUXER_VALIDATE_RESPONSES
Section titled “FLUXER_VALIDATE_RESPONSES”Defaults to on outside production. Response schema validation. Costs latency when forced on. A development setting, which Compose does not forward.
FLUXER_KLIPY_API_KEY
Section titled “FLUXER_KLIPY_API_KEY”Default empty. The Klipy GIF provider key. GIF search reports itself unavailable while this and the admin dashboard key are both empty. Also read by unfurl, which accepts KLIPY_API_KEY as a fallback. Compose forwards this name from .env, and not the fallback.
FLUXER_YOUTUBE_API_KEY
Section titled “FLUXER_YOUTUBE_API_KEY”Default empty. The YouTube Data API key. Also read by unfurl, which accepts YOUTUBE_API_KEY as a fallback. Compose forwards this name from .env, and not the fallback.
FLUXER_CACHE_PURGE_ADAPTER
Section titled “FLUXER_CACHE_PURGE_ADAPTER”Default none. How cached copies of deleted or replaced media are purged. none or http. Anything else fails startup. With none, nothing is queued or sent. Compose forwards it from .env.
FLUXER_CACHE_PURGE_HTTP_ENDPOINT
Section titled “FLUXER_CACHE_PURGE_HTTP_ENDPOINT”Default empty. Required when the adapter is http. Must be an absolute http or https URL without credentials, or startup fails. While purges are queued, the worker posts JSON {"exact": [...], "prefix": [...]} here every 10 seconds. Redirects are not followed. Any 2xx marks the batch done. Your endpoint then purges those URLs from every proxy that caches media. A 400 or 422 makes the worker resend each URL alone, and a URL refused alone moves to the cache_purge:rejected key. Any other failure is retried on a later run. Compose forwards it from .env.
FLUXER_CACHE_PURGE_HTTP_TOKEN
Section titled “FLUXER_CACHE_PURGE_HTTP_TOKEN”Default empty. The bearer token sent in the Authorization header of each purge request. Empty sends no header. When the adapter is http, a token with spaces, line breaks or non-ASCII characters fails startup. Compose forwards it from .env.
FLUXER_CACHE_PURGE_HTTP_TIMEOUT_MS
Section titled “FLUXER_CACHE_PURGE_HTTP_TIMEOUT_MS”Default 10000. Purge request timeout. Accepts 1000 to 10000. The range is checked only when the adapter is http. Compose forwards it from .env.
FLUXER_GEOIP_DB_PATH
Section titled “FLUXER_GEOIP_DB_PATH”Default empty. The GeoIP database. A filesystem path, or an s3://bucket/key URL whose download_path query parameter is mandatory and must be absolute. Read by api and worker. Compose forwards it from .env.
Instance identity and branding
Section titled “Instance identity and branding”Compose forwards every name below from .env. The admin dashboard also sets the product name and the brand assets, and a value saved there wins. All are optional.
A brand asset on another origin than the instance needs no CSP setting. app-proxy adds the origin of each of the five image URLs below to img-src, whether the URL is set here or in the dashboard. Content Security Policy has the timing.
app-proxy on a self-hosted instance also writes the product name, the icon, the favicon and the theme colour into the page it serves, so a browser shows them before the web app starts. The page title and the home screen name use the product name. The home screen icon uses the icon, or the favicon when no icon is set. The browser favicon and the /favicon.ico redirect use the favicon, or the icon when no favicon is set. The install manifest at /manifest.json takes the product name, the theme colour and the icon, and leaves out the Fluxer description. /browserconfig.xml, the Fluxer tile for legacy Edge, returns 404. app-proxy takes these values the same way as the image origins, from this environment at start and then from the dashboard.
FLUXER_APP_PRODUCT_NAME
Section titled “FLUXER_APP_PRODUCT_NAME”Default Fluxer. The product name clients display and the API writes in its emails and report flows. Also settable in the admin dashboard, which wins. Compose forwards it from .env.
FLUXER_APP_ICON_URL
Section titled “FLUXER_APP_ICON_URL”Default empty. The client icon. Its origin is added to the CSP by app-proxy. Compose forwards it from .env.
FLUXER_APP_SYMBOL_URL
Section titled “FLUXER_APP_SYMBOL_URL”Default empty. The symbol mark. Its origin is added to the CSP by app-proxy. Compose forwards it from .env.
FLUXER_APP_LOGO_URL
Section titled “FLUXER_APP_LOGO_URL”Default empty. The logo. Its origin is added to the CSP by app-proxy. Compose forwards it from .env.
FLUXER_APP_WORDMARK_URL
Section titled “FLUXER_APP_WORDMARK_URL”Default empty. The wordmark. Its origin is added to the CSP by app-proxy. Compose forwards it from .env.
FLUXER_APP_FAVICON_URL
Section titled “FLUXER_APP_FAVICON_URL”Default empty. The favicon. Its origin is added to the CSP by app-proxy. Compose forwards it from .env.
FLUXER_APP_THEME_COLOR
Section titled “FLUXER_APP_THEME_COLOR”Default empty. The theme colour, as a hex value such as #4641d9. The web app uses it as its brand colour and as the browser theme colour. A value that is not #rgb or #rrggbb is ignored, and so is pure black. Compose forwards it from .env.
FLUXER_APP_STATUS_PAGE_URL
Section titled “FLUXER_APP_STATUS_PAGE_URL”Default empty. The public status page. The web app links to it when loading stalls, and polls its Instatus summary.json and components.json for incidents and maintenance. Empty removes the link and stops the polling. app-proxy does not add this origin to the CSP, so a status page off fluxerstatus.com also needs it in FLUXER_CSP_EXTRA_CONNECT_SRC. Compose forwards it from .env.
FLUXER_APP_STATUS_PAGE_INCIDENT_HISTORY_URL
Section titled “FLUXER_APP_STATUS_PAGE_INCIDENT_HISTORY_URL”Default empty. The incident history page of the status page. Compose forwards it from .env.
FLUXER_INSTANCE_SETUP_CONFIGURED
Section titled “FLUXER_INSTANCE_SETUP_CONFIGURED”Default false. Whether setup is marked complete. The stored app_public_config row wins whenever its setup.configured is a boolean. This variable supplies the default only while no such row exists, and only on a self-hosted instance. Off a self-hosted instance the state is always true whatever this says. Compose forwards it from .env.
FLUXER_ACCOUNT_IDENTITY
Section titled “FLUXER_ACCOUNT_IDENTITY”Default empty, which means email when SMTP is set up and username otherwise. How members sign in on a new self-hosted instance, username or email. Case is ignored, and any other value fails startup. The API reads it once, on the first start of an instance with no stored sign-in method, and stores the result. An instance that is already in use is stored as email whatever this says, by the rules in Instances that already exist. The setup wizard can change the stored method until setup is complete or the first account exists, and after that nothing changes it. Off a self-hosted instance the method is always email. Compose forwards it from .env. See Sign-in methods.
FLUXER_TAG_STYLE
Section titled “FLUXER_TAG_STYLE”Default empty, which means none. How usernames are tagged on a new self-hosted email instance. none gives unique usernames with no tag, and random gives every account a random tag. A username instance always uses none and ignores this value. Case is ignored, and any other value fails startup. The API reads it once, together with FLUXER_ACCOUNT_IDENTITY, on the first start of an instance with no stored sign-in method. An instance that is already in use is stored as random whatever this says. The setup wizard can change it until setup is complete or the first account exists. Compose forwards it from .env. See Sign-in methods.
FLUXER_AUTO_JOIN_INVITE_CODE
Section titled “FLUXER_AUTO_JOIN_INVITE_CODE”Default empty. An invite every new account joins. Must be a live invite code. Compose forwards it from .env.
FLUXER_VISIONARIES_GUILD_ID
Section titled “FLUXER_VISIONARIES_GUILD_ID”Default empty. The guild that grants the visionary role. Snowflake. Compose forwards it from .env.
FLUXER_VISIONARIES_GUILD_VISIONARY_ROLE_ID
Section titled “FLUXER_VISIONARIES_GUILD_VISIONARY_ROLE_ID”Default empty. The role granted there. Snowflake. Compose forwards it from .env.
Completing setup saves that state independently of the environment default. Later branding or legal updates preserve it. Setting FLUXER_INSTANCE_SETUP_CONFIGURED back to false does not reopen the wizard or restore the unauthenticated setup access and first-registration admin grant described in Get started.
API and worker settings
Section titled “API and worker settings”FLUXER_API_WORKER_TASK is required under single_task. The rest are optional.
FLUXER_API_PORT
Section titled “FLUXER_API_PORT”Default 8080. The API listen port. Compose sets 8080, which the edge proxies to.
FLUXER_API_HEADERS_TIMEOUT_MS
Section titled “FLUXER_API_HEADERS_TIMEOUT_MS”Default 30000. How long a client may take to send the request line and the headers. Integer 1000 to 3600000, checked at startup. It is clamped down to FLUXER_API_REQUEST_TIMEOUT_MS, so raising it alone does nothing. Compose forwards it from .env.
FLUXER_API_REQUEST_TIMEOUT_MS
Section titled “FLUXER_API_REQUEST_TIMEOUT_MS”Default 120000. How long a client may take over the whole request. Integer 1000 to 3600000, checked at startup. This is the one to raise for large uploads or high latency links. Compose forwards it from .env.
FLUXER_API_WORKER_MODE
Section titled “FLUXER_API_WORKER_MODE”Default all_lanes. Which lanes the worker runs. all_lanes, single_lane, or single_task. Anything else fails startup. Compose sets all_lanes, because the stack runs one worker.
FLUXER_API_WORKER_LANE
Section titled “FLUXER_API_WORKER_LANE”No default. Which lane, under single_lane. realtime, unfurl, lifecycle, batch, or crosspost. Compose does not forward it. A deployment that splits the worker by lane needs a crosspost worker, or published announcements are never delivered.
FLUXER_API_WORKER_TASK
Section titled “FLUXER_API_WORKER_TASK”No default. Which task, under single_task. Must name a known task. Compose does not forward it.
FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER
Section titled “FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER”No default. Whether the cron scheduler runs. Compose sets true. Without it no scheduled job runs anywhere.
FLUXER_API_WORKER_LANE_CONCURRENCY_OVERRIDES
Section titled “FLUXER_API_WORKER_LANE_CONCURRENCY_OVERRIDES”No default. Per-lane concurrency. JSON object keyed by lane, realtime, unfurl, lifecycle, batch, or crosspost. Each value must be an integer of at least 1 or startup fails. Built-in concurrency is realtime 10, unfurl 20, lifecycle 8, batch 12, crosspost 8. Compose forwards it from .env.
FLUXER_NODE_EXTRA_CA_CERTS
Section titled “FLUXER_NODE_EXTRA_CA_CERTS”Default /etc/ssl/certs/ca-certificates.crt, the system bundle in the image. A PEM file of extra CA certificates that api and worker trust for every outbound TLS connection, such as SMTP, object storage, search and webhooks. Node trusts its own built-in roots plus this one file. A file you set replaces the system bundle, so append your CA to a copy of that bundle, mount it into both containers and point this at it. Compose passes it to api and worker as NODE_EXTRA_CA_CERTS, and keeps the image path when .env leaves it out. It reads the prefixed name so a NODE_EXTRA_CA_CERTS in the host shell does not reach the containers.
Media Proxy settings
Section titled “Media Proxy settings”The Media Proxy takes uploads, transforms images, and serves media back. All are optional.
FLUXER_MEDIA_PROXY_MODE
Section titled “FLUXER_MEDIA_PROXY_MODE”Default mp. Which routes are served. mp, static, upload, or relay. relay serves the upload relay and the operator and internal endpoints, and returns 404 for every read. Anything else is a startup error. Also settable with --mode. Compose sets upload, which the upload relay needs.
FLUXER_MEDIA_PROXY_HOST
Section titled “FLUXER_MEDIA_PROXY_HOST”Default 0.0.0.0. The bind address. Also settable with --bind-host. Compose does not set it, so the default applies.
FLUXER_MEDIA_PROXY_PORT
Section titled “FLUXER_MEDIA_PROXY_PORT”Default 8080. The listen port. Also settable with --port. Compose sets 8080, which the edge proxies to.
FLUXER_MEDIA_PROXY_READ_ONLY
Section titled “FLUXER_MEDIA_PROXY_READ_ONLY”Default false. Refuses writes. --read-only can force it on. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_STORAGE_BACKEND
Section titled “FLUXER_MEDIA_PROXY_STORAGE_BACKEND”Default local. Where objects live. local or s3. Compose sets s3, the bundled object store.
FLUXER_MEDIA_PROXY_STORAGE_ROOT
Section titled “FLUXER_MEDIA_PROXY_STORAGE_ROOT”Default ./media_proxy_storage. The local storage directory. Used only by the local backend, so Compose does not forward it.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_MAX_BODY_BYTES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_MAX_BODY_BYTES”Default 524288000. The upload size cap on both sides. Rust accepts 1 byte to 5 GiB and refuses to start when the value is above FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_MAX_TOTAL_BYTES. The effective cap is the smaller of the token’s value and this. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_TOKEN_TTL_SECS
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_TOKEN_TTL_SECS”Default 900. Relay token lifetime. Read by the API alone. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT”Default http://localhost:8088/media. The relay URL handed to clients. Node side only. A trailing slash and a trailing /v1/relay are stripped. Compose forwards it from .env, and builds it from the public origin and /media when it is unset.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_KEEP_DIRECT_COUNTRIES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_KEEP_DIRECT_COUNTRIES”Default empty. Countries that upload directly to S3. Node side only. Empty means every client uses the relay, which is what keeps the internal S3 address out of browsers. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_S3_TIMEOUT_MS
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_S3_TIMEOUT_MS”Default 900000. Relay upload timeout. Accepts 1000 to 3600000. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_BYTES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_BYTES”Default 33554432, 32 MiB. The largest declared upload the relay holds in memory so it can retry the S3 write. Accepts 0 to 268435456. A larger upload is streamed to S3 and gets no retry. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_TOTAL_BYTES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_TOTAL_BYTES”Default 536870912, 512 MiB. The memory all buffered relay uploads may hold at once. Accepts 0 to 8589934592. An upload that would pass it is streamed instead. The default equals the 512mb that FLUXER_MEDIA_PROXY_MEMORY_LIMIT gives media-proxy, so lower this or raise that limit when many uploads arrive at once. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_DIR
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_DIR”Defaults to the system temporary directory. Where relay bodies spool, and where media-proxy copies a stored audio or video object above 500 MiB when it has to read inside the file. That happens when the API asks for the media metadata or a frame of an upload or a stored object, and when an attachment read asks for a frame of a video. Must be writable. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_CHUNK_BYTES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_CHUNK_BYTES”Default 1048576. Spool chunk size. Accepts 64 KiB to 64 MiB. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_MAX_TOTAL_BYTES
Section titled “FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SPOOL_MAX_TOTAL_BYTES”Default 8589934592. Total spool ceiling, shared by relay bodies and the copies of stored audio and video above 500 MiB. A relay upload that does not fit beside the copies in progress fails. An upload larger than the ceiling is stored as a plain file without media metadata, and a stored video larger than the ceiling has no poster frame. Accepts 0 to 256 GiB, and must be at or above FLUXER_MEDIA_PROXY_UPLOAD_RELAY_MAX_BODY_BYTES, which puts the usable floor at 500 MiB by default. Compose forwards it from .env.
Sending an audio or video attachment above 500 MiB holds up to three copies of it on local disk at once. media-proxy holds one in the spool directory, and api holds the download and the copy it rewrites without metadata in its temporary directory. Leave free space of three times the attachment size limit for each send you expect at the same moment.
api waits 30 seconds for the metadata of an upload up to 500 MiB. Above that it waits 30 seconds plus one second for each 16 MiB of the upload, about 94 seconds for 1 GiB and 542 seconds for 8 GiB. In that time media-proxy copies the upload out of object storage, reads it, and hashes it. A deployment that cannot sustain 16 MiB per second through those steps stores the upload as a plain file.
FLUXER_MEDIA_PROXY_MAX_NATIVE_TRANSFORMS
Section titled “FLUXER_MEDIA_PROXY_MAX_NATIVE_TRANSFORMS”Defaults to available parallelism, clamped to 2 to 8. Concurrent image transforms. Accepts 1 to 128. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_WORKER_QUEUE_CAPACITY
Section titled “FLUXER_MEDIA_PROXY_WORKER_QUEUE_CAPACITY”Defaults to eight times the transform limit. Transform queue depth. Accepts 1 to 8192. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_BYTES
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_BYTES”Default 268435456. Transform cache size. Accepts 0 to 4 GiB. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_MAX_ENTRY_BYTES
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_MAX_ENTRY_BYTES”Default 67108864. Largest cacheable result. Accepts 0 to 512 MiB. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_TTL_MS
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_TTL_MS”Default 120000. Transform cache lifetime. Accepts 0 to 3600000. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_SOCKET_IO_TIMEOUT_MS
Section titled “FLUXER_MEDIA_PROXY_SOCKET_IO_TIMEOUT_MS”Default 30000. Socket read and write timeout. Accepts 0 to 300000. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_SHUTDOWN_GRACE_MS
Section titled “FLUXER_MEDIA_PROXY_SHUTDOWN_GRACE_MS”Default 30000. Milliseconds allowed to finish requests and media transforms after SIGTERM or Ctrl-C. Accepts 0 to 300000. Exceeding the deadline exits the process with an error. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_TRANSFORM_TIMEOUT_MS
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_TIMEOUT_MS”Default 15000. Per-transform timeout. Accepts 1000 to 120000. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_MAX_ENCODE_FRAMES
Section titled “FLUXER_MEDIA_PROXY_MAX_ENCODE_FRAMES”Default 20000. Animation frame ceiling. Accepts 1 to 100000. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_MAX_ENCODE_DURATION_MS
Section titled “FLUXER_MEDIA_PROXY_MAX_ENCODE_DURATION_MS”Default 30000. Animation duration ceiling. Accepts 100 to 600000. Compose forwards it from .env.
FLUXER_NSFW_SERVICE_ENDPOINT
Section titled “FLUXER_NSFW_SERVICE_ENDPOINT”Default empty. An external NSFW classifier. No such service ships with the stack. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_NSFW_THRESHOLD
Section titled “FLUXER_MEDIA_PROXY_NSFW_THRESHOLD”Default 0.85. The media-side NSFW cutoff. Accepts 0.0 to 1.0 and finite. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_CORS_MODE
Section titled “FLUXER_MEDIA_PROXY_CORS_MODE”Default off. The cross-origin read policy. off, report, or enforce. Anything else is a startup error. report logs each read whose origin would be refused and changes no response. enforce refuses those reads with 403 and sets Access-Control-Allow-Origin to the request origin. When FLUXER_MEDIA_PROXY_MODE is static or relay, the Media Proxy ignores it, so a bad value is no startup error. Compose forwards it from .env.
FLUXER_MEDIA_PROXY_CORS_ALLOWED_ORIGINS
Section titled “FLUXER_MEDIA_PROXY_CORS_ALLOWED_ORIGINS”Default empty. Comma-separated origins that may read media through CORS, such as https://web.fluxer.app. Spaces around an entry and empty entries are ignored. Each entry is parsed as a URL and must be http or https with no credentials, query, fragment, or path beyond /. Its host must be an IPv4 address, a bracketed IPv6 address, or a domain name whose labels are ASCII letters, digits, and hyphens after parsing. So a wildcard such as https://*.fluxer.app, a brace pattern, an underscore, a trailing dot on a domain name, or any non-ASCII character fails. Write an internationalised domain name in its punycode form. The one exception is the desktop app origin fluxer-app://app. It is accepted as an entry and is always allowed without one. Any failing entry makes the process exit. An entry is compared in the form a browser sends. https://Web.Fluxer.App:443/ matches https://web.fluxer.app. Required when FLUXER_MEDIA_PROXY_CORS_MODE is report or enforce. When the policy is report or enforce, the Media Proxy logs the parsed list once at startup. When FLUXER_MEDIA_PROXY_MODE is static or relay, the Media Proxy ignores it.
Compose forwards it from .env, and defaults it to the public origin, built from FLUXER_PUBLIC_ORIGIN, or from FLUXER_PUBLIC_SCHEME, FLUXER_DOMAIN, and FLUXER_PUBLIC_PORT. That origin is the only entry, so the web app the instance serves reads its own media. The hosted web client and the desktop app run on https://web.fluxer.app. To let them read this instance’s media through CORS, set the list in .env to the public origin and https://web.fluxer.app, such as https://chat.example.com,https://web.fluxer.app. A value set in .env replaces the Compose value, so it must name the public origin too.
FLUXER_MEDIA_PROXY_ATTACHMENT_SIGNATURE_MODE
Section titled “FLUXER_MEDIA_PROXY_ATTACHMENT_SIGNATURE_MODE”Default off. The signed attachment URL policy. off, report, or enforce. An empty value means off, and anything else is a startup error. report logs each attachment read enforce would refuse and serves it. enforce refuses those reads with 404. Under both, a read whose signature is valid gets a Cache-Control that ends with the signature instead of the usual year. report and enforce require FLUXER_MEDIA_PROXY_ATTACHMENT_URL_SECRETS_BASE64, and the Media Proxy logs the mode and the number of secrets once at startup. When FLUXER_MEDIA_PROXY_MODE is static or relay, the Media Proxy ignores it and the secrets, so a bad value is no startup error. A self-hosted instance leaves it off, and signs nothing, until an operator turns it on. Compose forwards it from .env.
This variable is the whole switch, and moving between the three states needs nothing else changed. The Media Proxy reads it once at start, so apply a change with docker compose up -d media-proxy, which replaces the container. docker compose restart media-proxy reuses the old environment. A cache or proxy in front of the instance has a switch of its own, and Turning the signature policy on gives the order to move the two in and what each pairing serves.
Gateway settings
Section titled “Gateway settings”The Gateway is the WebSocket service clients hold open for live events. All are optional.
FLUXER_GATEWAY_PORT
Section titled “FLUXER_GATEWAY_PORT”Default 8771. The listen port. Compose sets 8080, which the edge proxies to.
FLUXER_GATEWAY_ROLE
Section titled “FLUXER_GATEWAY_ROLE”Default all. Which subsystems this node runs. websocket, sessions, presence, guilds, calls, push, or all. An unrecognised value also becomes all. Compose does not forward it, because the stack runs one Gateway node.
FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT
Section titled “FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT”Default http://localhost:8088/media. The public media URL used in payloads. Compose forwards it from .env, and uses FLUXER_MEDIA_ENDPOINT or the public media URL when it is unset.
FLUXER_GATEWAY_STATIC_CDN_ENDPOINT
Section titled “FLUXER_GATEWAY_STATIC_CDN_ENDPOINT”Default http://localhost:8088. The public static origin used in payloads. Compose forwards it from .env, and uses FLUXER_STATIC_CDN_ENDPOINT or the public origin when it is unset.
FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_ENTRIES
Section titled “FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_ENTRIES”Default 128. Presence push buffer depth. Compose forwards it from .env.
FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_BYTES
Section titled “FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_BYTES”Default 1048576. Presence push buffer size. Compose forwards it from .env.
FLUXER_GATEWAY_PUSH_ENROLLED_CLEAR_NOTIFICATIONS_ENABLED
Section titled “FLUXER_GATEWAY_PUSH_ENROLLED_CLEAR_NOTIFICATIONS_ENABLED”Default true. Whether the Gateway asks push to clear a channel’s notifications once the channel is read. Compose forwards it from .env.
FLUXER_GATEWAY_PUSH_OUTBOX_REQUEST_TIMEOUT_MS
Section titled “FLUXER_GATEWAY_PUSH_OUTBOX_REQUEST_TIMEOUT_MS”Default 100000. How long the Gateway waits on one push outbox request. Milliseconds. Compose forwards it from .env.
FLUXER_GATEWAY_HTTP_FAILURE_THRESHOLD
Section titled “FLUXER_GATEWAY_HTTP_FAILURE_THRESHOLD”Default 6. Failures before the API circuit opens. Integer. Compose forwards it from .env.
FLUXER_GATEWAY_HTTP_RECOVERY_TIMEOUT_MS
Section titled “FLUXER_GATEWAY_HTTP_RECOVERY_TIMEOUT_MS”Default 15000. How long the circuit stays open. Milliseconds. Compose forwards it from .env.
FLUXER_GATEWAY_CLUSTER_ENABLED
Section titled “FLUXER_GATEWAY_CLUSTER_ENABLED”Default false. BEAM clustering. Single-node by default. Compose forwards none of the clustering names, because the stack runs one Gateway node.
FLUXER_GATEWAY_CLUSTER_DISCOVERY_DNS_NAME
Section titled “FLUXER_GATEWAY_CLUSTER_DISCOVERY_DNS_NAME”No default. The DNS name peers are discovered through. Needs clustering on.
FLUXER_GATEWAY_CLUSTER_DISCOVERY_NODE_BASENAME
Section titled “FLUXER_GATEWAY_CLUSTER_DISCOVERY_NODE_BASENAME”No default. The node basename for discovery. Needs clustering on.
FLUXER_GATEWAY_CLUSTER_DISCOVERY_POLL_INTERVAL_MS
Section titled “FLUXER_GATEWAY_CLUSTER_DISCOVERY_POLL_INTERVAL_MS”Default 5000. Discovery poll interval. Milliseconds.
FLUXER_GATEWAY_CLUSTER_STATIC_PEERS
Section titled “FLUXER_GATEWAY_CLUSTER_STATIC_PEERS”Default empty. A fixed peer list. Comma separated Erlang node names, capped at 256. An entry that is not a node name fails startup.
FLUXER_ERLANG_NODE_NAME
Section titled “FLUXER_ERLANG_NODE_NAME”Default [email protected]. The BEAM node name. Must be resolvable by peers when clustering. Compose does not forward it.
FLUXER_ERLANG_COOKIE
Section titled “FLUXER_ERLANG_COOKIE”No default. The BEAM distribution secret. The Gateway refuses to start without it, and Compose refuses to start the stack when .env lacks it. Anyone who reaches the distribution port with the value gets code execution, so keep the port unpublished.
FLUXER_ERLANG_DIST_PORT
Section titled “FLUXER_ERLANG_DIST_PORT”Default 8081. The BEAM distribution port. Not published or forwarded by Compose. Never expose it.
FLUXER_ERLANG_SCHEDULERS
Section titled “FLUXER_ERLANG_SCHEDULERS”Defaults to the container CPU count, clamped to 2 through 16. Normal scheduler count. Positive integer, passed to the BEAM as +S N:N. A value that is not a positive integer is ignored and the derivation runs instead. Compose forwards it from .env to gateway.
FLUXER_ERLANG_DIRTY_CPU_SCHEDULERS
Section titled “FLUXER_ERLANG_DIRTY_CPU_SCHEDULERS”Defaults to two thirds of the scheduler count, rounded up. Dirty CPU scheduler count. Positive integer, passed as +SDcpu N:N. Same fallback rule as FLUXER_ERLANG_SCHEDULERS. Compose forwards it from .env to gateway.
FLUXER_ERLANG_SCHEDULERS_MIN
Section titled “FLUXER_ERLANG_SCHEDULERS_MIN”Default 2. The floor of the scheduler clamp. Compose forwards it from .env to gateway.
FLUXER_ERLANG_SCHEDULERS_MAX
Section titled “FLUXER_ERLANG_SCHEDULERS_MAX”Default 16. The ceiling of the scheduler clamp. Compose forwards it from .env to gateway, and .env.example ships both names commented out.
The Gateway entrypoint derives the scheduler counts before the BEAM starts. It reads the container CPU quota, clamps the result between FLUXER_ERLANG_SCHEDULERS_MIN and FLUXER_ERLANG_SCHEDULERS_MAX, exports the answer as FLUXER_ERLANG_SCHEDULERS, and derives FLUXER_ERLANG_DIRTY_CPU_SCHEDULERS from it. vm.args.src then substitutes both into +S and +SDcpu.
The clamp bounds reach the Gateway from .env. To pin the count, set FLUXER_ERLANG_SCHEDULERS in .env, which skips the clamp.
The Gateway protocol version is 1. The Gateway answers any other value in ?v= with 101, then a close frame reading Invalid API version. Gateway overview has the close code.
The Gateway reads every FLUXER_GATEWAY_ name straight from the environment. Compose forwards each one a single-node stack can use. The role, the clustering names and FLUXER_GATEWAY_API_RPC_ENDPOINT need a Compose override file.
App proxy settings
Section titled “App proxy settings”app-proxy serves the web client. All are optional.
FLUXER_APP_PROXY_HOST
Section titled “FLUXER_APP_PROXY_HOST”Default 0.0.0.0. The bind address. The image sets the same value, and Compose does not set it.
FLUXER_APP_PROXY_PORT
Section titled “FLUXER_APP_PROXY_PORT”Default 8080. The listen port. Compose sets 8080, which the edge proxies to.
FLUXER_STATIC_DIR
Section titled “FLUXER_STATIC_DIR”Default ./static. Where the client’s SPA bundle lives, unrelated to static-proxy. A path inside the image, so Compose does not forward it.
FLUXER_APP_PROXY_INDEX_UPSTREAM_URL
Section titled “FLUXER_APP_PROXY_INDEX_UPSTREAM_URL”No default. An upstream to fetch index.html from. Leave unset for the shipped image. Compose forwards it from .env.
DISCOVERY_UPSTREAM_URL
Section titled “DISCOVERY_UPSTREAM_URL”Default http://localhost:8088/api/.well-known/fluxer. Where the self-hosted image fetches the discovery document, which it reads only for the branding image URLs. Compose sets http://edge:8088/.well-known/fluxer. That internal listener supplies the client-IP header the API requires, so leave it pointed at the edge.
DISCOVERY_REFRESH_INTERVAL_MS
Section titled “DISCOVERY_REFRESH_INTERVAL_MS”Default 60000. How often discovery is refetched, in milliseconds, and so the longest a branding image saved in the admin dashboard waits for its origin to reach the CSP. A value under 1000 counts as 1000. A failed fetch keeps the previous origins and is retried with a backoff of up to five minutes. Unprefixed name. Compose forwards it from .env.
FLUXER_APP_PROXY_SAME_ORIGIN_HOSTS
Section titled “FLUXER_APP_PROXY_SAME_ORIGIN_HOSTS”Default empty. Hostnames, comma separated, without a scheme, port or path. When a request’s Host is in the list, the page bootstrap points the web app and its API at that host, as https://<host> and https://<host>/api. Every other request gets the discovery values unchanged. Set it when the web app is served on more than one hostname and each hostname routes /api to the API. Invalid entries are logged and dropped. Compose forwards it from .env.
FLUXER_APP_PROXY_MANIFEST_SCOPE_EXTENSIONS
Section titled “FLUXER_APP_PROXY_MANIFEST_SCOPE_EXTENSIONS”Default empty. HTTPS origins, comma separated, without a path, query or credentials. app-proxy writes each one into the scope_extensions list of the web app manifest, so an installed web app keeps other origins inside its window. Empty leaves the list empty. Each listed origin must serve /.well-known/web-app-origin-association naming this web app, or browsers ignore the entry. Invalid entries are logged and dropped. Compose forwards it from .env.
Admin settings
Section titled “Admin settings”admin serves the dashboard at FLUXER_ADMIN_BASE_PATH, /admin by default. All are optional.
FLUXER_ADMIN_HOST
Section titled “FLUXER_ADMIN_HOST”Default 0.0.0.0. The bind address. Compose does not set it, so the default applies.
FLUXER_ADMIN_PORT
Section titled “FLUXER_ADMIN_PORT”Default 3020. The listen port. The image sets 8080 and Compose sets 8080, which the edge proxies to.
FLUXER_ADMIN_BASE_PATH
Section titled “FLUXER_ADMIN_BASE_PATH”Default empty. The path the dashboard is mounted under. Write it with a leading slash and no trailing slash, such as /panel. The edge and the Compose-built FLUXER_ADMIN_ENDPOINT use the value as written, and only admin itself normalises it. The service serves at root and re-prefixes every emitted URL with this. Read by admin and the edge. Compose forwards it from .env with a default of /admin, hands the same value to the edge, which routes that path to admin, and builds the default FLUXER_ADMIN_ENDPOINT from it. A change moves the admin OAuth redirect with it, and takes docker compose up -d.
FLUXER_ADMIN_OAUTH_REDIRECT_URI
Section titled “FLUXER_ADMIN_OAUTH_REDIRECT_URI”Defaults to the admin endpoint plus /oauth2_callback. The OAuth2 redirect. Must equal what the API derives from FLUXER_ADMIN_ENDPOINT, so Compose does not set it. The API does not read this name.
FLUXER_ADMIN_OAUTH_CLIENT_ID
Section titled “FLUXER_ADMIN_OAUTH_CLIENT_ID”Default 1234567890123456789. The OAuth2 client id of admin. Must equal the admin application id built into the API, so Compose does not set it.
FLUXER_BUILD_VERSION
Section titled “FLUXER_BUILD_VERSION”Defaults to the crate version. The reported build of admin and app-proxy. BUILD_VERSION is preferred over it. Compose does not set it.
Content Security Policy
Section titled “Content Security Policy”app-proxy builds the policy for the client HTML and the assets it serves. The client HTML policy allows each inline script by its SHA-256 hash, so every visitor to a host gets the same document and header. Each variable appends sources to one directive on top of the built-in ones. All are empty by default, and Compose forwards every one.
Every name below goes in .env. app-proxy reads its environment at container start, so a change takes effect on docker compose up -d app-proxy. A docker compose restart app-proxy does not apply it.
FLUXER_CSP_EXTRA_DEFAULT_SRC, FLUXER_CSP_EXTRA_CONNECT_SRC, FLUXER_CSP_EXTRA_IMG_SRC, FLUXER_CSP_EXTRA_MEDIA_SRC, FLUXER_CSP_EXTRA_FONT_SRC, FLUXER_CSP_EXTRA_SCRIPT_SRC, FLUXER_CSP_EXTRA_STYLE_SRC, FLUXER_CSP_EXTRA_FRAME_SRC, FLUXER_CSP_EXTRA_WORKER_SRC, and FLUXER_CSP_EXTRA_MANIFEST_SRC take one or more sources separated by commas, spaces, tabs, or newlines. Blank entries and sources the directive already lists are dropped. FLUXER_CSP_REPORT_URI sets a single report-uri value.
object-src, base-uri, and frame-ancestors are fixed and have no override. app-proxy adds the static CDN endpoint and the media endpoint from its environment. The self-hosted image also adds the origin of each branding image to img-src. At start it takes them from FLUXER_APP_ICON_URL, FLUXER_APP_SYMBOL_URL, FLUXER_APP_LOGO_URL, FLUXER_APP_WORDMARK_URL and FLUXER_APP_FAVICON_URL. It then polls the discovery document every DISCOVERY_REFRESH_INTERVAL_MS, where a value saved in the admin dashboard wins, so a dashboard change reaches the header within a minute by default and needs no restart. The header is the same for every visitor. A stack on one hostname therefore needs no extra sources, and neither does a logo or favicon on another domain. The usual reason to set one is a voice server on another hostname, which needs its WebSocket origin in FLUXER_CSP_EXTRA_CONNECT_SRC. Voice media does not use the proxy has that line in place.
A front proxy must not add a Content-Security-Policy of its own.
Keys in .env.example that no service reads
Section titled “Keys in .env.example that no service reads”Docker Compose or the edge container consumes most of the names below, and each of those reaches a service under a different name. Setting one outside Compose, in Kubernetes or a systemd unit, does nothing.
FLUXER_DOMAIN
Section titled “FLUXER_DOMAIN”Interpolated into FLUXER_BASE_DOMAIN and into the derived URL strings.
COMPOSE_FILE
Section titled “COMPOSE_FILE”Read by Docker Compose to select the proxy overlay.
FLUXER_EDGE_SITE_ADDRESS
Section titled “FLUXER_EDGE_SITE_ADDRESS”Read by the edge container only, and only in the bundled layout. Both overlays overwrite it, docker-compose.proxy.yml with :8080 and tunnel.compose.yml with :80.
FLUXER_CADDY_SITE_ADDRESS
Section titled “FLUXER_CADDY_SITE_ADDRESS”Read by Docker Compose as the default for FLUXER_EDGE_SITE_ADDRESS when that name is unset.
FLUXER_EDGE_TRUSTED_PROXIES and FLUXER_EDGE_ENCODE
Section titled “FLUXER_EDGE_TRUSTED_PROXIES and FLUXER_EDGE_ENCODE”Read by the edge container only. They take effect on docker compose up -d edge.
FLUXER_EDGE_BIND
Section titled “FLUXER_EDGE_BIND”Used as the host side of the overlay’s port mapping.
FLUXER_HTTP_PORT and FLUXER_HTTPS_PORT
Section titled “FLUXER_HTTP_PORT and FLUXER_HTTPS_PORT”Used as the host side of the edge’s published ports.
FLUXER_REGISTRY_OWNER, FLUXER_REGISTRY and FLUXER_IMAGE_TAG
Section titled “FLUXER_REGISTRY_OWNER, FLUXER_REGISTRY and FLUXER_IMAGE_TAG”Image name selection.
The third-party image names
Section titled “The third-party image names”Image name selection for the bundled services, listed under Images.
POSTGRES_PASSWORD
Section titled “POSTGRES_PASSWORD”Becomes FLUXER_POSTGRES_PASSWORD and the Postgres image’s own password.
MEILI_MASTER_KEY
Section titled “MEILI_MASTER_KEY”Becomes FLUXER_SEARCH_API_KEY and the Meilisearch image’s own key.
FLUXER_S3_ACCESS_KEY and FLUXER_S3_SECRET_KEY
Section titled “FLUXER_S3_ACCESS_KEY and FLUXER_S3_SECRET_KEY”Become FLUXER_S3_ACCESS_KEY_ID and FLUXER_S3_SECRET_ACCESS_KEY, and the identity seaweedfs-init installs.
LIVEKIT_API_KEY and LIVEKIT_API_SECRET
Section titled “LIVEKIT_API_KEY and LIVEKIT_API_SECRET”Become FLUXER_LIVEKIT_API_KEY and FLUXER_LIVEKIT_API_SECRET, and LiveKit’s own LIVEKIT_KEYS.
The extra CA bundle
Section titled “The extra CA bundle”FLUXER_NODE_EXTRA_CA_CERTS becomes NODE_EXTRA_CA_CERTS in api and worker.
The LiveKit configuration names
Section titled “The LiveKit configuration names”FLUXER_LIVEKIT_LOG_LEVEL, FLUXER_LIVEKIT_TCP_PORT, FLUXER_LIVEKIT_UDP_PORT, FLUXER_LIVEKIT_USE_EXTERNAL_IP, FLUXER_LIVEKIT_NODE_IP, FLUXER_LIVEKIT_STUN_PRIMARY and FLUXER_LIVEKIT_STUN_SECONDARY are written into LIVEKIT_CONFIG, and the two ports also into the published port mappings.
The per-service pool sizes
Section titled “The per-service pool sizes”FLUXER_API_POSTGRES_MAX_CONNECTIONS, FLUXER_WORKER_POSTGRES_MAX_CONNECTIONS, FLUXER_USERS_SHARD_POSTGRES_MAX_CONNECTIONS and FLUXER_MESSAGES_SHARD_POSTGRES_MAX_CONNECTIONS each become FLUXER_POSTGRES_MAX_CONNECTIONS in their own service.
Memory, tuning, health checks and restarts
Section titled “Memory, tuning, health checks and restarts”The names under Resources and Health checks and restarts are read by Docker Compose, which hands them to the engine or to the bundled services under their own names.
Keys Compose does not forward
Section titled “Keys Compose does not forward”Every other name on this page takes effect from .env, except the names Compose fills from another name, listed first below. The other names below stay out of it for the reason given. Add one through a Compose override file that puts it in the relevant service’s environment, which Keep a local compose change describes.
Names Compose fills from another name
Section titled “Names Compose fills from another name”Compose writes each of these from the .env name given, so setting the name itself in .env has no effect. FLUXER_POSTGRES_PASSWORD comes from POSTGRES_PASSWORD. FLUXER_POSTGRES_MAX_CONNECTIONS comes from FLUXER_API_POSTGRES_MAX_CONNECTIONS, FLUXER_WORKER_POSTGRES_MAX_CONNECTIONS, FLUXER_USERS_SHARD_POSTGRES_MAX_CONNECTIONS or FLUXER_MESSAGES_SHARD_POSTGRES_MAX_CONNECTIONS, one per service. FLUXER_S3_ACCESS_KEY_ID comes from FLUXER_S3_ACCESS_KEY, and FLUXER_S3_SECRET_ACCESS_KEY from FLUXER_S3_SECRET_KEY. FLUXER_SEARCH_API_KEY comes from MEILI_MASTER_KEY. FLUXER_LIVEKIT_API_KEY comes from LIVEKIT_API_KEY, and FLUXER_LIVEKIT_API_SECRET from LIVEKIT_API_SECRET. FLUXER_BASE_DOMAIN comes from FLUXER_DOMAIN.
Fixed wiring
Section titled “Fixed wiring”FLUXER_ENV, FLUXER_SELF_HOSTED, FLUXER_DATABASE_BACKEND, the listen ports FLUXER_API_PORT, FLUXER_GATEWAY_PORT, FLUXER_MEDIA_PROXY_PORT, FLUXER_APP_PROXY_PORT and FLUXER_ADMIN_PORT, the internal endpoints FLUXER_INTERNAL_API_ENDPOINT, FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT and FLUXER_MEDIA_PROXY_ENDPOINT, the FLUXER_API_ENDPOINT of admin, FLUXER_SVC_NAME, FLUXER_SVC_MODE, FLUXER_SVC_SHARD_ID, FLUXER_SVC_SHARD_COUNT, FLUXER_API_WORKER_MODE, FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER, FLUXER_MEDIA_PROXY_MODE and FLUXER_MEDIA_PROXY_STORAGE_BACKEND. Compose sets each to a fixed value that the edge, the health checks or another service depend on. A different value gives no working stack.
Names the stack has no use for
Section titled “Names the stack has no use for”FLUXER_MEDIA_PROXY_HOST, FLUXER_APP_PROXY_HOST, FLUXER_ADMIN_HOST and FLUXER_PUSH_SERVICE_HOST, whose default already listens on every interface. FLUXER_PUSH_SERVICE_PORT, whose default the health check of push reads. FLUXER_SVC_LISTEN_HOST and FLUXER_SVC_PORT, which the health checks address. FLUXER_API_WORKER_LANE and FLUXER_API_WORKER_TASK, which only the single-lane worker modes read. FLUXER_MEDIA_PROXY_STORAGE_ROOT, which only the local backend reads. FLUXER_S3_BUCKET_STATIC, which only the static mode of media-proxy reads. FLUXER_GATEWAY_ROLE, the FLUXER_GATEWAY_CLUSTER_ names, FLUXER_ERLANG_NODE_NAME and FLUXER_ERLANG_DIST_PORT, since the stack runs one Gateway node. FLUXER_GATEWAY_API_RPC_ENDPOINT, which the Gateway derives from the internal API address. FLUXER_SNOWFLAKE_SERVICE_SUBJECT, FLUXER_USERS_SERVICE_SUBJECT and FLUXER_GIF_SERVICE_SUBJECT, which have to match the subjects the internal services listen on. FLUXER_ADMIN_OAUTH_REDIRECT_URI, which has to match what the API derives. FLUXER_ADMIN_OAUTH_CLIENT_ID, which has to match the admin application id built into the API. The relay-mode names of push, FLUXER_PUSH_RELAY_MAX_CONCURRENT, FLUXER_PUSH_RELAY_MAX_BODY_BYTES, FLUXER_PUSH_RELAY_TRUSTED_PROXY_HOPS, FLUXER_PUSH_RELAY_SOURCE_BUCKET_ENABLED and the _ENTRIES, _PER_MINUTE and _BURST names under FLUXER_PUSH_RELAY_TOKEN_BUCKET and FLUXER_PUSH_RELAY_SOURCE_BUCKET, since the stack runs push in delivery mode. FLUXER_STATIC_DIR, a path inside the image. FLUXER_DOCS_ENDPOINT, which only the docs server reads.
Allocator and image library tuning
Section titled “Allocator and image library tuning”LD_PRELOAD and MALLOC_CONF in media-proxy and app-proxy, and VIPS_CONCURRENCY, VIPS_DISC_THRESHOLD and G_MESSAGES_DEBUG in media-proxy. Each image sets them. LD_PRELOAD loads jemalloc and MALLOC_CONF tunes it. VIPS_CONCURRENCY stays at 1 on purpose, and FLUXER_MEDIA_PROXY_MAX_NATIVE_TRANSFORMS is the setting for image work in parallel. VIPS_DISC_THRESHOLD, 1g in the image, is the image size above which libvips decodes to disk instead of memory. Lower it when a small FLUXER_MEDIA_PROXY_MEMORY_LIMIT runs out of memory.
Build and platform metadata
Section titled “Build and platform metadata”BUILD_VERSION, FLUXER_BUILD_VERSION and RELEASE_CHANNEL describe the shipped build. POD_NAME and HOSTNAME come from the platform.
Test and development switches
Section titled “Test and development switches”FLUXER_TEST_MODE_ENABLED, FLUXER_TEST_HARNESS_TOKEN, FLUXER_DISABLE_RATE_LIMITS, FLUXER_VALIDATE_RESPONSES and FLUXER_RELAX_REGISTRATION_RATE_LIMITS. None of them belongs on a production instance.
The Cassandra names
Section titled “The Cassandra names”Every FLUXER_CASSANDRA_ name. The stack runs Postgres.
Older names of a forwarded setting
Section titled “Older names of a forwarded setting”FLUXER_NATS_CORE_URL, KLIPY_API_KEY and YOUTUBE_API_KEY. A service reads each only when the name it replaces is unset or empty, and Compose forwards that name instead.
Individual Stripe price names
Section titled “Individual Stripe price names”The FLUXER_STRIPE_PRICE_MONTHLY_, FLUXER_STRIPE_PRICE_YEARLY_, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_ and FLUXER_STRIPE_PRICE_GIFT_1_YEAR_ names, one per currency. FLUXER_STRIPE_PRICES holds the same prices in one JSON object, and the admin dashboard can replace them.
Runtime settings in the admin dashboard
Section titled “Runtime settings in the admin dashboard”The admin dashboard at FLUXER_ADMIN_BASE_PATH stores these in the database. They apply without recreating containers, and a value set here wins over the matching environment variable.
Instance Config, Public App Identity
Section titled “Instance Config, Public App Identity”Product name, client-visible brand assets, the terms, privacy, and community guidelines links, and the setup state in the instance discovery document. No environment variable sets a legal link. On a self-hosted instance, the enforcement emails list only the terms and guidelines links set here, and the clients and report flows show no guidelines link until one is set. Its emails and its IP block errors name no contact address. They tell the reader to contact the administrators of the instance, and no setting adds an address. The emails, the report flows, the IP block errors, the title of an APNs alert that has none, and the built-in admin application use the product name set here, and a rename applies without a restart.
Instance Config, Registration Controls
Section titled “Instance Config, Registration Controls”Registration mode of open, approval, or closed, admin-issued registration URLs, and pending approval requests.
Instance Config, Bot protection
Section titled “Instance Config, Bot protection”The ALTCHA proof-of-work check on sign-up, login, password recovery, and a few other gated operations. It is on by default. Turn it off here, or change its cost and maximum counter.
Instance Config, Community & Policy
Section titled “Instance Config, Community & Policy”Single-community mode, direct messages and friends, the premium model, and optional embed services.
Instance Config, Runtime Integrations
Section titled “Instance Config, Runtime Integrations”The Klipy GIF key, the YouTube Data API key, email delivery with an SMTP connection test, and Bluesky OAuth. Email delivery and its test show only on an email instance.
Instance Config, Premium & billing
Section titled “Instance Config, Premium & billing”The premium tier name and information link, the billing switch, the Stripe secret key and webhook secret, the webhook URL to register in Stripe, and the price catalogue with its default currency, country currencies and legacy prices. See Payments.
Instance Config, Media Expiry
Section titled “Instance Config, Media Expiry”Size-based attachment lifetimes. The built-in default is on.
Turning expiry off hides every expiry date at once and stops deletion. The stored dates are kept.
On a self-hosted instance, changes also apply to attachments that were uploaded earlier. Turning expiry on, or changing a size or lifetime rule, starts a background job that works through existing messages in chunks and recalculates the expiry of their attachments. No attachment is deleted while that job runs. An attachment whose date passed while expiry was off, or that the new settings would expire sooner, is kept for at least the renew window first. Until the job reaches a message, its attachments show the state they had before, so one whose date passed while expiry was off shows as expired until then.
The official hosted instance does not run that job. There, a change applies to attachments uploaded from then on, and deletion of expired attachments is never paused.
Instance Config, Gateway Rollout Configuration
Section titled “Instance Config, Gateway Rollout Configuration”Session and guild rollout percentages, RPC timeouts, and Gateway concurrency.
Instance Config, Single Sign-On (SSO)
Section titled “Instance Config, Single Sign-On (SSO)”OIDC-style SSO for the client and admin apps, and whether SSO is enforced.
Limit Config
Section titled “Limit Config”The instance limits published to clients.
Voice Regions and Voice Servers
Section titled “Voice Regions and Voice Servers”The voice regions offered and the servers behind them.
Admin API Keys
Section titled “Admin API Keys”Credentials for the Admin API.
Changes propagate without a restart. A process that cannot apply an update logs the error and keeps its previous valid settings.
These settings exist only in the dashboard: attachment decay, which defaults to on, and the inactivity deletion threshold, which defaults to 365 days.
Services
Section titled “Services”The stack runs its containers on one Docker bridge network, which is private to the stack.
| Service | Image | What it does |
|---|---|---|
| edge | caddy:2.11-alpine | TLS and path routing, the only HTTP entry point |
| app-proxy | fluxer-app-proxy-self-hosted | Serves the web client and builds its CSP header |
| static-proxy | fluxer-static | Serves the static asset bundle |
| api | fluxer-api | The HTTP API |
| worker | fluxer-api | Background lanes and the cron scheduler |
| gateway | fluxer-gateway | The Gateway WebSocket |
| media-proxy | fluxer-media-proxy | Uploads, transforms, and media delivery |
| push | fluxer-push | Push notification delivery |
| admin | fluxer-admin | The admin dashboard |
| snowflakes, snowflakes-shard | fluxer-snowflakes | Identifier allocation |
| users, users-shard | fluxer-users | User reads and writes |
| messages, messages-shard | fluxer-messages | Message reads and writes |
| gifs, gifs-shard | fluxer-gifs | GIF provider access |
| unfurl, unfurl-shard | fluxer-unfurl | Link unfurling |
| postgres | postgres:16-alpine | The database |
| valkey | valkey/valkey:9.1-alpine | The key-value store and pub/sub bus, and the deletion queues |
| nats | nats:2.14-alpine | Core messaging, and the JetStream streams holding queued background jobs |
| meilisearch | getmeili/meilisearch:v1.53 | The search index |
| seaweedfs | chrislusf/seaweedfs:4.47 | S3-compatible object storage |
| seaweedfs-init | chrislusf/seaweedfs:4.47 | Creates the buckets and the S3 identity, then exits |
| livekit | livekit/livekit-server:v1.12.0 | Voice and video |
The third-party images above are the defaults of the names under Images.
The edge and LiveKit are the only services that publish ports. The edge publishes 80/tcp, 443/tcp, 443/udp, or one plain-HTTP port under the overlay. LiveKit publishes 7881/tcp and 7882/udp. Everything else is reachable only over the bridge network.
Most settings reach api, worker, gateway, media-proxy, push, admin and the internal services through one shared environment block, so one line in .env reaches every one of them that reads it. app-proxy takes its own list, since it reads the static, media and S3 upload origins, the branding image URLs and its own names. The edge takes the FLUXER_EDGE_ variables and FLUXER_ADMIN_BASE_PATH. The third-party services take their image name, their memory limit, and their own tuning: postgres the password, database, role and FLUXER_POSTGRES_ server settings, meilisearch the master key and FLUXER_MEILISEARCH_ values, valkey the FLUXER_VALKEY_ values, seaweedfs its heap ceiling and telemetry switch, and livekit the key pair, the port, STUN and log level variables. static-proxy reads no environment variables.
The internal services each run a router, which takes requests and holds no state, and one shard, which holds the caches and the database connections. The shipped stack fixes FLUXER_SVC_SHARD_COUNT at 1.
Resources
Section titled “Resources”Every service has a memory limit and four also have a memory reservation, all under deploy.resources. Compose reads gb as 1024 MiB and mb as 1 MiB, so 5gb is 5368709120 bytes. Plain docker compose up applies both keys on a single host, with no Swarm and no --compatibility flag. The engine rejects any limit below 6mb, and rejects a limit lower than the same service’s reservation with Minimum memory limit can not be less than memory reservation limit.
A limit is a ceiling. The limits below sum to 18.5 GiB and the stack does not need a host that large, because each container uses only the memory it allocates, up to its limit.
deploy.resources.reservations.memory becomes the container’s cgroup v2 memory.low, which biases kernel reclaim towards other containers under host pressure. It reserves nothing on its own.
All are optional.
FLUXER_CADDY_MEMORY_LIMIT
Section titled “FLUXER_CADDY_MEMORY_LIMIT”Default 256mb. The ceiling for edge.
FLUXER_POSTGRES_MEMORY_LIMIT
Section titled “FLUXER_POSTGRES_MEMORY_LIMIT”Default 5gb. The ceiling for postgres. Must be at or above FLUXER_POSTGRES_MEMORY_RESERVATION or the container fails to create. Shared memory in use under FLUXER_POSTGRES_SHM_SIZE is charged against it.
FLUXER_VALKEY_MEMORY_LIMIT
Section titled “FLUXER_VALKEY_MEMORY_LIMIT”Default 256mb. The ceiling for valkey. Must stay above FLUXER_VALKEY_MAXMEMORY, which bounds the stored dataset alone.
FLUXER_NATS_MEMORY_LIMIT
Section titled “FLUXER_NATS_MEMORY_LIMIT”Default 256mb. The ceiling for nats. Covers JetStream file store metadata as well as the core server.
FLUXER_MEILISEARCH_MEMORY_LIMIT
Section titled “FLUXER_MEILISEARCH_MEMORY_LIMIT”Default 1536mb. The ceiling for meilisearch. Indexing a backlog of a few hundred thousand messages with two indexing threads peaks near 1 GB, so keep this above 1 GB.
FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY
Section titled “FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY”Default 256mb. Becomes MEILI_MAX_INDEXING_MEMORY. The memory the indexer may use for one batch. Meilisearch uses more than this while indexing, because every indexing thread also has buffers of its own. Lower it whenever you lower FLUXER_MEILISEARCH_MEMORY_LIMIT.
FLUXER_MEILISEARCH_MAX_INDEXING_THREADS
Section titled “FLUXER_MEILISEARCH_MAX_INDEXING_THREADS”Default 2. Becomes MEILI_MAX_INDEXING_THREADS. Without it Meilisearch indexes on half the host’s CPU threads, and each thread can use up to 150 MB of buffers, so a host with many cores runs out of memory under a container limit that suits a small one. Raise it only together with FLUXER_MEILISEARCH_MEMORY_LIMIT.
FLUXER_MEILISEARCH_ENV
Section titled “FLUXER_MEILISEARCH_ENV”Default production. Becomes MEILI_ENV. production requires the master key on every request.
FLUXER_MEILISEARCH_NO_ANALYTICS
Section titled “FLUXER_MEILISEARCH_NO_ANALYTICS”Default true. Becomes MEILI_NO_ANALYTICS, which keeps Meilisearch from sending usage data.
FLUXER_SEAWEEDFS_MEMORY_LIMIT
Section titled “FLUXER_SEAWEEDFS_MEMORY_LIMIT”Default 2gb. The ceiling for seaweedfs. One process runs the master, the volume server and the S3 gateway. The peak is the parts of one upload in flight at once, which the client uploads in parallel: a 500 MB attachment is 20 parts of 25 MB.
FLUXER_SEAWEEDFS_GOMEMLIMIT
Section titled “FLUXER_SEAWEEDFS_GOMEMLIMIT”Default 1536MiB. The heap ceiling the Go runtime collects against. Go cannot see the container limit, so without this value an upload burst grows the heap past FLUXER_SEAWEEDFS_MEMORY_LIMIT and the kernel OOM-kills the container mid-upload with exit 137. Raising FLUXER_SEAWEEDFS_MEMORY_LIMIT on its own does not fix that, because the runtime grows to fill whatever it is given. Keep this near three quarters of the container limit and move the two together.
FLUXER_SEAWEEDFS_TELEMETRY
Section titled “FLUXER_SEAWEEDFS_TELEMETRY”Default false. Becomes the -master.telemetry flag of seaweedfs, which reports usage to the SeaweedFS project when on.
FLUXER_SEAWEEDFS_VOLUME_GROWTH
Section titled “FLUXER_SEAWEEDFS_VOLUME_GROWTH”Default 1. Becomes WEED_MASTER_VOLUME_GROWTH_COPY_1, the number of volumes SeaweedFS creates at once when a bucket runs out of room. Each volume reserves a 1 GB slot of free disk, and the stack writes to four buckets. SeaweedFS’s own default of 7 needs about 28 GB free before every bucket has a volume, so on a smaller disk the first bucket takes every slot and uploads fail with No writable volumes and no free volumes left.
FLUXER_SEAWEEDFS_INIT_MEMORY_LIMIT
Section titled “FLUXER_SEAWEEDFS_INIT_MEMORY_LIMIT”Default 128mb. The ceiling for seaweedfs-init. A one-shot container that exits, so it never overlaps steady state.
FLUXER_LIVEKIT_MEMORY_LIMIT
Section titled “FLUXER_LIVEKIT_MEMORY_LIMIT”Default 512mb. The ceiling for livekit. Grows with the number of concurrent voice publishers.
FLUXER_API_MEMORY_LIMIT
Section titled “FLUXER_API_MEMORY_LIMIT”Default 2560mb. The ceiling for api. Node derives its heap ceiling from this at about half, and the derivation stops falling at or below 512mb.
FLUXER_WORKER_MEMORY_LIMIT
Section titled “FLUXER_WORKER_MEMORY_LIMIT”Default 2560mb. The ceiling for worker. Same Node derivation as api, applied to every background lane at once.
FLUXER_GATEWAY_MEMORY_LIMIT
Section titled “FLUXER_GATEWAY_MEMORY_LIMIT”Default 1gb. The ceiling for gateway. The BEAM has no heap ceiling of its own, so this limit is the only bound on the Gateway.
FLUXER_MEDIA_PROXY_MEMORY_LIMIT
Section titled “FLUXER_MEDIA_PROXY_MEMORY_LIMIT”Default 512mb. The ceiling for media-proxy. Image and video transforms decode into this ceiling, so a large upload is what reaches this limit.
FLUXER_PUSH_MEMORY_LIMIT
Section titled “FLUXER_PUSH_MEMORY_LIMIT”Default 256mb. The ceiling for push. Raise it alongside FLUXER_PUSH_SERVICE_QUEUE_CAPACITY or FLUXER_PUSH_SERVICE_SEND_CONCURRENCY.
FLUXER_STATIC_PROXY_MEMORY_LIMIT
Section titled “FLUXER_STATIC_PROXY_MEMORY_LIMIT”Default 256mb. The ceiling for static-proxy. The service reads no environment variables and serves files only.
FLUXER_APP_PROXY_MEMORY_LIMIT
Section titled “FLUXER_APP_PROXY_MEMORY_LIMIT”Default 256mb. The ceiling for app-proxy. It keeps the branding image origins from discovery in memory and reads no database.
FLUXER_SNOWFLAKES_MEMORY_LIMIT
Section titled “FLUXER_SNOWFLAKES_MEMORY_LIMIT”Default 128mb. The ceiling for snowflakes. A router holds no shard state.
FLUXER_SNOWFLAKES_SHARD_MEMORY_LIMIT
Section titled “FLUXER_SNOWFLAKES_SHARD_MEMORY_LIMIT”Default 256mb. The ceiling for snowflakes-shard. Holds the identifier buffer sized by FLUXER_SNOWFLAKE_SERVICE_BATCH_SIZE.
FLUXER_USERS_MEMORY_LIMIT
Section titled “FLUXER_USERS_MEMORY_LIMIT”Default 128mb. The ceiling for users. A router holds no shard state.
FLUXER_USERS_SHARD_MEMORY_LIMIT
Section titled “FLUXER_USERS_SHARD_MEMORY_LIMIT”Default 256mb. The ceiling for users-shard. Holds the read cache bounded by FLUXER_SVC_CACHE_MAX_ENTRIES, which defaults to 100000 entries.
FLUXER_GIFS_MEMORY_LIMIT
Section titled “FLUXER_GIFS_MEMORY_LIMIT”Default 128mb. The ceiling for gifs. A router holds no shard state.
FLUXER_GIFS_SHARD_MEMORY_LIMIT
Section titled “FLUXER_GIFS_SHARD_MEMORY_LIMIT”Default 256mb. The ceiling for gifs-shard. Lower than the 536870912 byte default of FLUXER_GIFS_SHARD_CACHE_MAX_BYTES.
FLUXER_MESSAGES_MEMORY_LIMIT
Section titled “FLUXER_MESSAGES_MEMORY_LIMIT”Default 128mb. The ceiling for messages. A router holds no shard state.
FLUXER_MESSAGES_SHARD_MEMORY_LIMIT
Section titled “FLUXER_MESSAGES_SHARD_MEMORY_LIMIT”Default 256mb. The ceiling for messages-shard. The shard has a Postgres pool of 20 connections alongside the read cache.
FLUXER_UNFURL_MEMORY_LIMIT
Section titled “FLUXER_UNFURL_MEMORY_LIMIT”Default 128mb. The ceiling for unfurl. A router holds no shard state.
FLUXER_UNFURL_SHARD_MEMORY_LIMIT
Section titled “FLUXER_UNFURL_SHARD_MEMORY_LIMIT”Default 256mb. The ceiling for unfurl-shard. Fetches remote pages, so a slow upstream holds bytes for the length of the fetch.
FLUXER_ADMIN_MEMORY_LIMIT
Section titled “FLUXER_ADMIN_MEMORY_LIMIT”Default 256mb. The ceiling for admin. Serves the dashboard and proxies no media.
A reservation goes to the services whose loss takes the instance down, so the kernel reclaims from everything else first. All are optional.
FLUXER_POSTGRES_MEMORY_RESERVATION
Section titled “FLUXER_POSTGRES_MEMORY_RESERVATION”Default 3gb. The reclaim floor for postgres. Must be at or below FLUXER_POSTGRES_MEMORY_LIMIT. Lowering the limit alone fails container creation.
FLUXER_API_MEMORY_RESERVATION
Section titled “FLUXER_API_MEMORY_RESERVATION”Default 1gb. The reclaim floor for api. Must be at or below FLUXER_API_MEMORY_LIMIT.
FLUXER_WORKER_MEMORY_RESERVATION
Section titled “FLUXER_WORKER_MEMORY_RESERVATION”Default 1gb. The reclaim floor for worker. Must be at or below FLUXER_WORKER_MEMORY_LIMIT.
FLUXER_GATEWAY_MEMORY_RESERVATION
Section titled “FLUXER_GATEWAY_MEMORY_RESERVATION”Default 384mb. The reclaim floor for gateway. Must be at or below FLUXER_GATEWAY_MEMORY_LIMIT.
Node sizes its own old-space heap at roughly half the container limit, with a floor near 259 MB. A container limit of 2560mb gives a 1328 MB heap ceiling, 1280mb gives 664 MB, 768mb gives 396 MB, and every limit at or under 512mb gives the same 259 MB. Pin the value only to move it away from that derivation. All are optional.
FLUXER_API_NODE_HEAP_MB
Section titled “FLUXER_API_NODE_HEAP_MB”No default. The V8 old-space ceiling for api, in MB. Appended to NODE_OPTIONS as --max-old-space-size only when non-empty. Keep it below FLUXER_API_MEMORY_LIMIT.
FLUXER_WORKER_NODE_HEAP_MB
Section titled “FLUXER_WORKER_NODE_HEAP_MB”No default. The V8 old-space ceiling for worker, in MB. Same rule against FLUXER_WORKER_MEMORY_LIMIT.
FLUXER_API_NODE_OPTIONS
Section titled “FLUXER_API_NODE_OPTIONS”No default. Extra Node flags for api, such as --dns-result-order=ipv4first. Compose reads it from .env and appends it to NODE_OPTIONS after --enable-source-maps and the heap flag, only when non-empty.
FLUXER_WORKER_NODE_OPTIONS
Section titled “FLUXER_WORKER_NODE_OPTIONS”No default. Extra Node flags for worker. Same rule as FLUXER_API_NODE_OPTIONS.
The names on the Postgres command line tune the bundled server. Keep them consistent with FLUXER_POSTGRES_MEMORY_LIMIT. All are optional.
FLUXER_POSTGRES_SERVER_MAX_CONNECTIONS
Section titled “FLUXER_POSTGRES_SERVER_MAX_CONNECTIONS”Default 150. The server-wide connection ceiling. The shipped pools total 90, from 25 each for api and worker and 20 each for messages-shard and users-shard. A value under about 95 exhausts the server before the pools fill, and the connections it turns away are refused with too many clients already.
FLUXER_POSTGRES_SHARED_BUFFERS
Section titled “FLUXER_POSTGRES_SHARED_BUFFERS”Default 512MB. The shared buffer pool. Allocated at server start, so it is charged to the container whether or not it is used.
FLUXER_POSTGRES_EFFECTIVE_CACHE_SIZE
Section titled “FLUXER_POSTGRES_EFFECTIVE_CACHE_SIZE”Default 2GB. The planner’s assumption about disk cache. Allocates nothing. Lowering it makes the planner prefer sequential scans and frees no memory.
FLUXER_POSTGRES_WORK_MEM
Section titled “FLUXER_POSTGRES_WORK_MEM”Default 8MB. The per-operation sort and hash budget. Charged per sort or hash node, so one complex query can spend several multiples of it.
FLUXER_POSTGRES_MAINTENANCE_WORK_MEM
Section titled “FLUXER_POSTGRES_MAINTENANCE_WORK_MEM”Default 256MB. The budget for one VACUUM, CREATE INDEX, or ALTER TABLE. One such operation at a time normally holds it.
FLUXER_POSTGRES_AUTOVACUUM_WORK_MEM
Section titled “FLUXER_POSTGRES_AUTOVACUUM_WORK_MEM”Default 128MB. The budget for each autovacuum worker. Postgres runs three workers by default, so budget three times this value.
FLUXER_POSTGRES_SHM_SIZE
Section titled “FLUXER_POSTGRES_SHM_SIZE”Default 1gb. The shared memory of the postgres container, used by parallel queries and parallel maintenance. Keep it well above FLUXER_POSTGRES_MAINTENANCE_WORK_MEM. A manual VACUUM sizes its shared memory from that budget, and fails with could not resize shared memory segment when it does not fit.
The rest of the Postgres command line has one variable per setting.
FLUXER_POSTGRES_RANDOM_PAGE_COST: default1.1, setsrandom_page_cost.FLUXER_POSTGRES_EFFECTIVE_IO_CONCURRENCY: default200, setseffective_io_concurrency.FLUXER_POSTGRES_DEFAULT_STATISTICS_TARGET: default200, setsdefault_statistics_target.FLUXER_POSTGRES_JIT: defaultoff, setsjit.FLUXER_POSTGRES_MIN_WAL_SIZE: default512MB, setsmin_wal_size.FLUXER_POSTGRES_MAX_WAL_SIZE: default2GB, setsmax_wal_size.FLUXER_POSTGRES_CHECKPOINT_COMPLETION_TARGET: default0.9, setscheckpoint_completion_target.FLUXER_POSTGRES_WAL_BUFFERS: default16MB, setswal_buffers.FLUXER_POSTGRES_WAL_COMPRESSION: defaultzstd, setswal_compression.FLUXER_POSTGRES_BGWRITER_DELAY: default50ms, setsbgwriter_delay.FLUXER_POSTGRES_BGWRITER_LRU_MAXPAGES: default1000, setsbgwriter_lru_maxpages.FLUXER_POSTGRES_AUTOVACUUM_VACUUM_SCALE_FACTOR: default0.05, setsautovacuum_vacuum_scale_factor.FLUXER_POSTGRES_AUTOVACUUM_ANALYZE_SCALE_FACTOR: default0.02, setsautovacuum_analyze_scale_factor.FLUXER_POSTGRES_AUTOVACUUM_VACUUM_COST_LIMIT: default2000, setsautovacuum_vacuum_cost_limit.FLUXER_POSTGRES_TRACK_IO_TIMING: defaulton, setstrack_io_timing.FLUXER_POSTGRES_SHARED_PRELOAD_LIBRARIES: defaultpg_stat_statements, setsshared_preload_libraries.
shared_preload_libraries is read only when the server starts. Add a library only if the image ships it, or Postgres refuses to start.
The bundled Valkey uses persistent storage. These settings control its memory limit, its behaviour when full, and how often it writes to disk.
FLUXER_VALKEY_MAXMEMORY
Section titled “FLUXER_VALKEY_MAXMEMORY”Default 192mb. The dataset ceiling. Bounds stored keys only. Client buffers, replication buffers and allocator overhead sit outside it and inside FLUXER_VALKEY_MEMORY_LIMIT.
FLUXER_VALKEY_MAXMEMORY_POLICY
Section titled “FLUXER_VALKEY_MAXMEMORY_POLICY”Default noeviction. An over-limit write returns an OOM error. Keep this policy to protect deletion queues and other shared state. See Volumes and buckets for recovery implications.
FLUXER_VALKEY_APPENDFSYNC
Section titled “FLUXER_VALKEY_APPENDFSYNC”Default everysec. How often Valkey flushes its append-only file to disk. always, everysec or no. everysec can lose the last second of writes in a crash.
No service sets a CPU limit, a CPU reservation or cpu_shares, so every container sees the host’s full CPU count. Bound the Gateway’s scheduler count with FLUXER_ERLANG_SCHEDULERS_MIN and FLUXER_ERLANG_SCHEDULERS_MAX, or pin it with FLUXER_ERLANG_SCHEDULERS and FLUXER_ERLANG_DIRTY_CPU_SCHEDULERS from Gateway settings.
On a host smaller than the 16 GB the defaults assume, lower the large ceilings and the Postgres tuning together. The block below is the 8 GB profile.
FLUXER_POSTGRES_MEMORY_LIMIT=2560mbFLUXER_POSTGRES_MEMORY_RESERVATION=1gbFLUXER_POSTGRES_SHARED_BUFFERS=384MBFLUXER_POSTGRES_EFFECTIVE_CACHE_SIZE=1536MBFLUXER_API_MEMORY_LIMIT=1280mbFLUXER_API_MEMORY_RESERVATION=768mbFLUXER_WORKER_MEMORY_LIMIT=1280mbFLUXER_WORKER_MEMORY_RESERVATION=768mbFLUXER_GATEWAY_MEMORY_LIMIT=512mbFLUXER_GATEWAY_MEMORY_RESERVATION=256mbFLUXER_MEILISEARCH_MEMORY_LIMIT=1gbFLUXER_MEILISEARCH_MAX_INDEXING_MEMORY=192mbFLUXER_SEAWEEDFS_MEMORY_LIMIT=1gbFLUXER_SEAWEEDFS_GOMEMLIMIT=768MiBFLUXER_GIFS_SHARD_CACHE_MAX_BYTES=134217728At 8 GB the api and worker heap ceilings fall to 664 MB each and Postgres caches less of the working set, which shows up as slower search and slower history scrolling under load. Nothing is turned off, though the largest attachments need the default 2gb SeaweedFS ceiling to upload every part at once.
The 4 GB profile trades more.
FLUXER_POSTGRES_MEMORY_LIMIT=1280mbFLUXER_POSTGRES_MEMORY_RESERVATION=512mbFLUXER_POSTGRES_SHARED_BUFFERS=192MBFLUXER_POSTGRES_EFFECTIVE_CACHE_SIZE=768MBFLUXER_POSTGRES_WORK_MEM=4MBFLUXER_POSTGRES_SERVER_MAX_CONNECTIONS=120FLUXER_API_MEMORY_LIMIT=768mbFLUXER_API_MEMORY_RESERVATION=512mbFLUXER_WORKER_MEMORY_LIMIT=640mbFLUXER_WORKER_MEMORY_RESERVATION=384mbFLUXER_GATEWAY_MEMORY_LIMIT=384mbFLUXER_GATEWAY_MEMORY_RESERVATION=192mbFLUXER_MEILISEARCH_MEMORY_LIMIT=1gbFLUXER_MEILISEARCH_MAX_INDEXING_MEMORY=128mbFLUXER_MEILISEARCH_MAX_INDEXING_THREADS=1FLUXER_MEDIA_PROXY_MEMORY_LIMIT=320mbFLUXER_SEAWEEDFS_MEMORY_LIMIT=320mbFLUXER_SEAWEEDFS_GOMEMLIMIT=240MiBFLUXER_LIVEKIT_MEMORY_LIMIT=320mbFLUXER_VALKEY_MEMORY_LIMIT=192mbFLUXER_VALKEY_MAXMEMORY=128mbFLUXER_GIFS_SHARD_CACHE_MAX_BYTES=67108864At 4 GB the api heap ceiling is 396 MB and the worker heap ceiling is 332 MB. That covers chat. A large attachment and a search reindex at the same time exceed those heap ceilings. Media transforms above roughly 20 MB start failing in media-proxy, Meilisearch indexes a backlog more slowly, and a busy voice room is the first thing to drop. Keep FLUXER_POSTGRES_SERVER_MAX_CONNECTIONS at or above 110.
Health checks and restarts
Section titled “Health checks and restarts”Compose reads these, and no Fluxer service does. The health checks decide when a dependent service may start and when Docker marks a container unhealthy. All are optional.
FLUXER_RESTART_POLICY: defaultunless-stopped, for every long-running service.seaweedfs-initnever restarts.FLUXER_HEALTHCHECK_INTERVAL: default10s, for every check.FLUXER_HEALTHCHECK_TIMEOUT: default5s, for every check.FLUXER_HEALTHCHECK_RETRIES: default10, foredge,postgres,valkey,nats,meilisearch,livekitandstatic-proxy.FLUXER_APP_HEALTHCHECK_RETRIES: default30, for the internal services,api,gateway,pushandadmin.FLUXER_APP_HEALTHCHECK_START_PERIOD: default90s, forapi,workerandgateway.FLUXER_SVC_HEALTHCHECK_START_PERIOD: default60s, for the internal services,pushandadmin.FLUXER_WORKER_HEALTHCHECK_RETRIES: default3, forworker. Its check reads a heartbeat file, and a stale one means the worker is stuck.FLUXER_SEAWEEDFS_HEALTHCHECK_RETRIES: default20, forseaweedfs.FLUXER_SEAWEEDFS_HEALTHCHECK_START_PERIOD: default60s, forseaweedfs.api,workerandmedia-proxywait onseaweedfs-init, which waits on this check.FLUXER_SEAWEEDFS_INIT_ATTEMPTS: default60. Compose forwards it from.envtoseaweedfs-init, which checks and creates the buckets that many times, 2 seconds apart, before it gives up. Raise it whendocker compose upfails onseaweedfs-initon a slow disk.
Durations take the Compose form, such as 90s or 2m. Raise the retries or start period on a slow host where a service is still starting when its check gives up.
Routing
Section titled “Routing”The edge is the only HTTP entry point, and every route is served from the one public hostname. It rewrites each path before handing it to an upstream. What the single port routes has the path table, and What every proxy must do has the requirements for anything in front of the edge.
FLUXER_ADMIN_BASE_PATH moves the admin path, and no other variable on this page changes that routing. The Caddyfile is a bind mount, so an edit to it takes docker compose restart edge.
CORS origins are exactly the app endpoint, the FLUXER_APP_ORIGIN_ALIASES origins, and the marketing endpoint. Serving the client from a hostname other than FLUXER_DOMAIN requires overriding FLUXER_APP_ENDPOINT.
Volumes and buckets
Section titled “Volumes and buckets”| Volume | Holds | Back up |
|---|---|---|
| postgres-data | Every account, message, and configuration row | Yes |
| seaweedfs-data | Every uploaded file | Yes |
| valkey-data | Pending file deletions, the thread auto archive schedule, other queues and shared cache | Yes |
| nats-data | Pending and failed background jobs | Yes |
| edge-data | Issued TLS certificates | Optional, a loss only costs a re-issue |
| edge-config | The edge’s own state | No |
| meilisearch-data | The search index, rebuildable from Postgres | Optional |
Losing valkey-data can delay scheduled account and bulk-message deletions while their queues are rebuilt. Pending asset deletions and cache purges exist only here and are lost with it, so do not treat this volume as disposable cache. The thread auto archive schedule exists only here too. After a loss, an active thread is archived automatically again only once it is modified or unarchived. Thread lifecycle states the archive rule.
Losing meilisearch-data leaves search empty until it is rebuilt. Nothing rebuilds it automatically, because Postgres still records every channel as indexed. Rebuild each index with POST /admin/search/indexes/{index_name}/refreshes. The channel_messages, guild_members and threads indexes rebuild one community at a time and need a guild_id for each. A backup of the volume avoids those calls on a large instance.
nats-data retains pending jobs in JOBS for up to 7 days and failed jobs in JOBS_DLQ for up to 30 days. A full jobs stream rejects new work. A full dead-letter stream drops its oldest entries, so investigate failures promptly. If dead-letter storage is unavailable, failed jobs remain in JOBS only until they expire. Losing this volume loses queued work, which is not automatically recovered from the database.
Startup refuses an existing JOBS or JOBS_DLQ stream with the wrong name, subjects, retention or storage type, or with sealing, no-ack, mirroring, sources, republishing or a subject transform set. Startup never recreates a stream. When the JOBS limits differ, startup updates them in place, unless tightening them would drop queued jobs. Stop publishers and workers before migrating an incompatible stream.
Volume names are prefixed with the Compose project name, so postgres-data is fluxer_postgres-data on the host.
seaweedfs-init creates these buckets and exits. Each bucket takes its name from its variable in .env, and the table gives the default.
| Bucket | Set by | Holds |
|---|---|---|
| fluxer | FLUXER_S3_BUCKET_CDN | Avatars, guild and entity assets, themes, entrance sounds, memes, and processed attachments |
| fluxer-uploads | FLUXER_S3_BUCKET_UPLOADS | Raw attachment uploads, before processing |
| fluxer-reports | FLUXER_S3_BUCKET_REPORTS | Abuse report evidence |
| fluxer-harvests | FLUXER_S3_BUCKET_HARVESTS | User and guild data archives |
FLUXER_S3_BUCKET_STATIC names an optional bucket. The shipped stack does not use or create it.