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 passes only variables listed in docker-compose.yml. For an unlisted setting, add it to the service’s environment through a local Compose override. Adding it to .env alone has no effect.
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. Leave an unwanted override unset, since an empty value does not always restore the default.
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.
FLUXER_INTERNAL_SCHEME
Section titled “FLUXER_INTERNAL_SCHEME”Default http. The scheme for internal service URLs. Must be http or https, and anything else fails startup.
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 refuses to start when one is unset or empty, and names the variable. Generate each with openssl rand -hex 32, except the relay secret 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_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. Hex fails. Must match on api, worker, and media-proxy. The API and the Media Proxy both check it at boot and refuse to start without it in upload mode.
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. The docs endpoint is a fixed value. 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 |
| docs | Always https://fluxer.dev. Not built from the domain settings |
| 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 is forced to https with no port.
FLUXER_INVITE_DOMAIN
Section titled “FLUXER_INVITE_DOMAIN”Default empty. The host used in invite links. A hostname with no scheme and no path.
FLUXER_GIFT_DOMAIN
Section titled “FLUXER_GIFT_DOMAIN”Default empty. The host used in gift links. A hostname with no scheme and no path.
FLUXER_API_ENDPOINT
Section titled “FLUXER_API_ENDPOINT”Defaults to the derived endpoint. The public API base. The admin service reads the same name as its internal API target, which is why Compose sets it to http://api:8080 for that container only.
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.
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.
FLUXER_GATEWAY_ENDPOINT
Section titled “FLUXER_GATEWAY_ENDPOINT”Defaults to the derived endpoint. The public Gateway WebSocket URL. Also the source of the internal Gateway URL when FLUXER_INTERNAL_GATEWAY_ENDPOINT is unset, with ws rewritten to http.
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.
FLUXER_STATIC_CDN_ENDPOINT
Section titled “FLUXER_STATIC_CDN_ENDPOINT”Defaults to the derived endpoint. The static asset origin. Read by api, worker, admin, app-proxy, and unfurl.
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.
The admin dashboard sets its cookie flags from the scheme of this value, which Compose builds from FLUXER_PUBLIC_SCHEME. 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 https://fluxer.dev. The docs origin. Never handed to a client.
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.
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.
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.
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. The API address used by worker and gateway. Compose sets http://api:8080.
FLUXER_INTERNAL_GATEWAY_ENDPOINT
Section titled “FLUXER_INTERNAL_GATEWAY_ENDPOINT”No default. The Gateway address the API calls. Falls back to the public Gateway URL with the scheme rewritten. Compose sets http://gateway: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.
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.
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.
FLUXER_UNFURL_STATIC_CDN_ENDPOINT
Section titled “FLUXER_UNFURL_STATIC_CDN_ENDPOINT”Falls back to FLUXER_STATIC_CDN_ENDPOINT. The static origin used by unfurl. Read only by unfurl.
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 follow FLUXER_PUBLIC_PORT, so a non-default public port moves them with it.
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, FLUXER_DOMAIN and FLUXER_PUBLIC_PORT. 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.
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.
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 port FLUXER_PUBLIC_PORT already names, because that publishes one host port twice and the edge refuses to start. Not read under the overlay, which publishes FLUXER_EDGE_BIND instead.
FLUXER_HTTPS_PORT
Section titled “FLUXER_HTTPS_PORT”Defaults to FLUXER_PUBLIC_PORT. 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 sets true for every service that merges the shared environment block, which app-proxy does not. 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, app-proxy, and gateway. It must match what the edge sets.
FLUXER_CLIENT_IP_HEADER
Section titled “FLUXER_CLIENT_IP_HEADER”No default. Alias for FLUXER_CLIENT_IP_HEADER_NAME. Accepted only by admin and app-proxy.
CLIENT_IP_HEADER_NAME and CLIENT_IP_HEADER
Section titled “CLIENT_IP_HEADER_NAME and CLIENT_IP_HEADER”No default. Legacy unprefixed aliases. Accepted only by admin and app-proxy, last in the preference chain.
FLUXER_API_IP_BAN_EXEMPT_IPS
Section titled “FLUXER_API_IP_BAN_EXEMPT_IPS”Default empty. Addresses exempt from IP bans. Comma separated. Every entry must parse as an IP or the API fails at boot.
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.
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. docker-compose.yml pins the caddy, postgres, valkey, nats, meilisearch, seaweedfs, and livekit images, and .env cannot change them.
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.
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.
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.
FLUXER_POSTGRES_PORT
Section titled “FLUXER_POSTGRES_PORT”Default 5432. The database port. Integer 1 to 65535.
FLUXER_POSTGRES_DATABASE
Section titled “FLUXER_POSTGRES_DATABASE”Default fluxer. The database name. Must be non-empty.
FLUXER_POSTGRES_USERNAME
Section titled “FLUXER_POSTGRES_USERNAME”Default fluxer. The role. Must be non-empty.
FLUXER_POSTGRES_PASSWORD
Section titled “FLUXER_POSTGRES_PASSWORD”Default fluxer. The password. The literal fluxer is rejected in production. CHANGE_ME is not.
FLUXER_POSTGRES_SSL
Section titled “FLUXER_POSTGRES_SSL”Default false. TLS to the database. Must be true or false. In production it must be true unless FLUXER_SELF_HOSTED=true.
FLUXER_POSTGRES_SSL_CA
Section titled “FLUXER_POSTGRES_SSL_CA”Default empty. A CA certificate in PEM form. Used only when SSL is on.
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. Compose sets 25 for api and worker and 20 for the database-backed shards.
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.
FLUXER_POSTGRES_PREPARED_STATEMENTS
Section titled “FLUXER_POSTGRES_PREPARED_STATEMENTS”Default true. Named prepared statements. Must be true or false. Compose passes 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 and does not include a Cassandra or Scylla service.
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. The admin service defaults to empty instead. Compose sets redis://valkey:6379/0.
FLUXER_KV_MODE
Section titled “FLUXER_KV_MODE”Default standalone. Use standalone for a single Valkey server or cluster for a Redis-compatible cluster.
FLUXER_KV_PROVIDER
Section titled “FLUXER_KV_PROVIDER”Default redis, which is the only accepted value.
FLUXER_SVC_CACHE_TTL_MS
Section titled “FLUXER_SVC_CACHE_TTL_MS”Default 30000. Soft cache lifetime in the internal services. Milliseconds. The users and messages routers ignore this setting because they keep no configurable response cache. Their shard caches still honour it.
FLUXER_SVC_CACHE_HARD_TTL_MS
Section titled “FLUXER_SVC_CACHE_HARD_TTL_MS”Default 600000. Hard cache lifetime. Clamped to at least the soft TTL.
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.
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.
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 disables the timer.
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.
FLUXER_S3_ENDPOINT
Section titled “FLUXER_S3_ENDPOINT”Default http://localhost:3900. The S3 API address. media-proxy defaults to empty instead.
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.
FLUXER_S3_FORCE_PATH_STYLE
Section titled “FLUXER_S3_FORCE_PATH_STYLE”Default false. Path-style addressing. media-proxy defaults to true instead. Compose sets true.
FLUXER_S3_REGION
Section titled “FLUXER_S3_REGION”Default local. The region string. media-proxy defaults to us-east-1. Compose sets us-east-1.
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.
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.
FLUXER_S3_SESSION_TOKEN
Section titled “FLUXER_S3_SESSION_TOKEN”Default empty. A temporary session token. Read by media-proxy only.
FLUXER_S3_BUCKET_CDN
Section titled “FLUXER_S3_BUCKET_CDN”Default fluxer. Processed assets. media-proxy defaults to cdn.
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.
FLUXER_S3_BUCKET_DOWNLOADS
Section titled “FLUXER_S3_BUCKET_DOWNLOADS”Default fluxer-downloads. Desktop build artefacts. Read by api and worker.
FLUXER_S3_BUCKET_REPORTS
Section titled “FLUXER_S3_BUCKET_REPORTS”Default fluxer-reports. Abuse report evidence. Read by api and worker.
FLUXER_S3_BUCKET_HARVESTS
Section titled “FLUXER_S3_BUCKET_HARVESTS”Default fluxer-harvests. Data archives. Read by api and worker.
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.
A separate downloads provider is available. FLUXER_S3_DOWNLOADS_ENDPOINT, FLUXER_S3_DOWNLOADS_PUBLIC_ENDPOINT, FLUXER_S3_DOWNLOADS_FORCE_PATH_STYLE, FLUXER_S3_DOWNLOADS_REGION, FLUXER_S3_DOWNLOADS_ACCESS_KEY_ID, and FLUXER_S3_DOWNLOADS_SECRET_ACCESS_KEY take effect only when FLUXER_S3_DOWNLOADS_ENDPOINT is non-empty, and they then replace the primary configuration for the downloads bucket.
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.
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.
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.
FLUXER_S3_READ_SIGNED
Section titled “FLUXER_S3_READ_SIGNED”Default false. Whether read requests are signed. Read by media-proxy only. Compose sets true, because seaweedfs-init installs an S3 identity and the store then refuses anonymous reads.
Compose sets AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, and AWS_EC2_METADATA_DISABLED for the AWS SDKs. No Fluxer code reads them directly.
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 sets meilisearch.
FLUXER_SEARCH_URL
Section titled “FLUXER_SEARCH_URL”Default http://127.0.0.1:9200. The engine address. Compose sets 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.
FLUXER_SEARCH_USERNAME
Section titled “FLUXER_SEARCH_USERNAME”Default empty. Basic auth user. Elasticsearch only. Ignored under Meilisearch.
FLUXER_SEARCH_PASSWORD
Section titled “FLUXER_SEARCH_PASSWORD”Default empty. Basic auth password. Elasticsearch only.
FLUXER_SEARCH_TLS_REJECT_UNAUTHORIZED
Section titled “FLUXER_SEARCH_TLS_REJECT_UNAUTHORIZED”Default true. Certificate verification. Elasticsearch only. Never passed to the Meilisearch client.
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 supplies the connection settings. 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.
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. The core NATS address. Not read by the Gateway.
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 uses FLUXER_NATS_URL unless this setting is supplied separately.
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. A separate variable from FLUXER_NATS_URL.
FLUXER_GATEWAY_API_RPC_ENDPOINT
Section titled “FLUXER_GATEWAY_API_RPC_ENDPOINT”No default. Where the Gateway calls the API. Read by the Gateway.
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.
FLUXER_SVC_MODE
Section titled “FLUXER_SVC_MODE”Default router. Must be router or shard. Any other value prevents startup.
FLUXER_SVC_SHARD_COUNT
Section titled “FLUXER_SVC_SHARD_COUNT”Default 1. How many shards exist. Fixed at 1 in the shipped stack.
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.
FLUXER_SVC_LISTEN_HOST
Section titled “FLUXER_SVC_LISTEN_HOST”Default 0.0.0.0. The bind address. Serves health and metrics only.
FLUXER_SVC_PORT
Section titled “FLUXER_SVC_PORT”Default 8090. The health and metrics port. Not published.
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 FLUXER_SVC_MAX_CONCURRENT_REQUESTS to users, users-shard, messages, and messages-shard at a default of 20, which pairs with their 20-connection Postgres pools, and leaves the other containers on the built-in defaults.
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.
For custom service routing, add these settings to the API container environment. The bundled Compose file does not forward them:
- Snowflake service:
FLUXER_SNOWFLAKE_SERVICE_SUBJECTandFLUXER_SNOWFLAKE_SERVICE_NATS_CLIENT_NAME. - Users service:
FLUXER_USERS_SERVICE_SUBJECTandFLUXER_USERS_SERVICE_NATS_CLIENT_NAME. - GIF service:
FLUXER_GIF_SERVICE_SUBJECTandFLUXER_GIF_SERVICE_NATS_CLIENT_NAME.
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_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 sets true.
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. 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 sets http://livekit:7880.
FLUXER_LIVEKIT_WEBHOOK_URL
Section titled “FLUXER_LIVEKIT_WEBHOOK_URL”Default empty. Where LiveKit posts webhooks. The API accepts requests on this route without user authentication and without a client-IP header.
FLUXER_LIVEKIT_DEFAULT_REGION
Section titled “FLUXER_LIVEKIT_DEFAULT_REGION”No default. The default voice region. JSON with id, name, emoji, latitude, and longitude.
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. 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.
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.
Voice reconciliation has moved from worker to the separate recon service. The old FLUXER_API_WORKER_ENABLE_VOICE_RECONCILIATION and FLUXER_API_WORKER_VOICE_RECONCILIATION_* settings no longer control it. The bundled Compose stack does not start the recon service.
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.
FLUXER_EMAIL_PROVIDER
Section titled “FLUXER_EMAIL_PROVIDER”.env.example none. The transport. smtp or none. Anything else fails startup.
FLUXER_EMAIL_FROM_EMAIL
Section titled “FLUXER_EMAIL_FROM_EMAIL”.env.example noreply@example.com. The sender address. Compose falls back to noreply@localhost.
FLUXER_EMAIL_FROM_NAME
Section titled “FLUXER_EMAIL_FROM_NAME”.env.example Fluxer. The sender name. An empty value sets an empty sender name.
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.
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.
FLUXER_EMAIL_SMTP_PORT
Section titled “FLUXER_EMAIL_SMTP_PORT”.env.example 587. The SMTP port. Falls back to 587.
FLUXER_EMAIL_SMTP_USERNAME
Section titled “FLUXER_EMAIL_SMTP_USERNAME”.env.example empty. The SMTP login. Part of the completeness check.
FLUXER_EMAIL_SMTP_PASSWORD
Section titled “FLUXER_EMAIL_SMTP_PASSWORD”.env.example empty. The SMTP password. Part of the completeness check.
FLUXER_EMAIL_SMTP_SECURE
Section titled “FLUXER_EMAIL_SMTP_SECURE”.env.example true. Implicit TLS. Defaults to true whenever the provider is smtp.
The API reads FLUXER_EMAIL_WEBHOOK_SECRET for inbound delivery webhooks. Neither .env.example nor docker-compose.yml has it.
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”All are optional.
| Variable | Value in .env.example | Controls |
|---|---|---|
| FLUXER_CAPTCHA_ENABLED | false | The CAPTCHA switch. Startup fails when it is true without a provider and that provider’s keys |
| FLUXER_CAPTCHA_PROVIDER | none | The provider. hcaptcha, turnstile, or none. Anything else fails startup |
FLUXER_CAPTCHA_HCAPTCHA_SITE_KEY, FLUXER_CAPTCHA_HCAPTCHA_SECRET_KEY, FLUXER_CAPTCHA_TURNSTILE_SITE_KEY, and FLUXER_CAPTCHA_TURNSTILE_SECRET_KEY ship empty in .env.example and the shipped Compose file forwards them all. Set the pair the selected provider needs. The admin dashboard’s Runtime Integrations panel sets the same provider and keys, and a value stored there wins over the environment. An instance that configures CAPTCHA only there must leave FLUXER_CAPTCHA_ENABLED at false, because the boot check reads the environment alone.
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.
FLUXER_PASSKEY_RP_ID
Section titled “FLUXER_PASSKEY_RP_ID”Defaults to FLUXER_BASE_DOMAIN. The WebAuthn relying party identifier. Changing it invalidates every passkey already registered.
FLUXER_PASSKEY_RP_NAME
Section titled “FLUXER_PASSKEY_RP_NAME”Default Fluxer. The relying party name browsers display. Does not follow FLUXER_DOMAIN.
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 defaults it to the public web app origin. When the variable is omitted outside Compose, the API accepts the configured app origin alongside the built-in Fluxer web and Android origins. An empty list selects only the configured app origin.
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. Add them to the API container environment. The shipped Compose file does not forward them.
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 derives admin@ followed by FLUXER_DOMAIN when it is unset |
The Gateway reads the same names. A malformed pair, or a private key that does not derive the public point, does not stop it. It records the fault in its log at startup and then drops every web push notification.
FLUXER_GATEWAY_PUSH_ENABLED is read by the Gateway alone, defaults to true, and skips that startup check on the VAPID pair when it is false.
Mobile push
Section titled “Mobile push”Both api and gateway read the APNs and FCM names. None appear in .env.example or in docker-compose.yml, so configuring mobile push means editing the Compose file. All are optional.
FLUXER_PUSH_APNS_ENABLED
Section titled “FLUXER_PUSH_APNS_ENABLED”Default false. The APNs switch. Must be set on both api and gateway.
FLUXER_PUSH_APNS_TEAM_ID
Section titled “FLUXER_PUSH_APNS_TEAM_ID”No default. The Apple team. Paired with the key ID.
FLUXER_PUSH_APNS_KEY_ID
Section titled “FLUXER_PUSH_APNS_KEY_ID”No default. The signing key ID. Paired with the team ID.
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.
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.
FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT
Section titled “FLUXER_PUSH_APNS_DEFAULT_ENVIRONMENT”Default production. Which APNs environment is used. production or development.
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.
FLUXER_PUSH_FCM_ENABLED
Section titled “FLUXER_PUSH_FCM_ENABLED”Default false. The FCM switch. Must be set on both api and gateway.
FLUXER_PUSH_FCM_PROJECT_ID
Section titled “FLUXER_PUSH_FCM_PROJECT_ID”No default. The Firebase project. Paired with the client email.
FLUXER_PUSH_FCM_CLIENT_EMAIL
Section titled “FLUXER_PUSH_FCM_CLIENT_EMAIL”No default. The service account address. Paired with the project.
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.
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.
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.
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.
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.
Payments
Section titled “Payments”Stripe billing, which the shipped stack keeps off. All are optional.
FLUXER_STRIPE_ENABLED
Section titled “FLUXER_STRIPE_ENABLED”Default false. The Stripe switch. Compose hardcodes false, so a self-hosted instance cannot enable it from .env.
FLUXER_STRIPE_SECRET_KEY
Section titled “FLUXER_STRIPE_SECRET_KEY”Default empty. The Stripe secret key. Not forwarded by the shipped Compose file.
FLUXER_STRIPE_WEBHOOK_SECRET
Section titled “FLUXER_STRIPE_WEBHOOK_SECRET”Default empty. The webhook signing secret. Not forwarded by the shipped Compose file.
FLUXER_STRIPE_PRICES
Section titled “FLUXER_STRIPE_PRICES”Default {}. Every price ID at once. JSON object. An individual price variable overrides the matching price in this object.
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, NOK, PLN, SEK, and TRY, plus FLUXER_STRIPE_PRICE_VISIONARY_ and FLUXER_STRIPE_PRICE_GIFT_VISIONARY_ in USD and EUR.
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. Existing subscriptions on these prices keep renewing. The localised checkout catalogue uses FLUXER_STRIPE_PRICES alone.
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.
Moderation and abuse
Section titled “Moderation and abuse”All are optional.
FLUXER_NCMEC_ENABLED
Section titled “FLUXER_NCMEC_ENABLED”Default false. NCMEC reporting. Compose hardcodes false.
FLUXER_NCMEC_BASE_URL
Section titled “FLUXER_NCMEC_BASE_URL”Default empty. The reporting endpoint. Required when reporting is on.
FLUXER_NCMEC_USERNAME
Section titled “FLUXER_NCMEC_USERNAME”Default empty. The reporting login. Required when reporting is on.
FLUXER_NCMEC_PASSWORD
Section titled “FLUXER_NCMEC_PASSWORD”Default empty. The reporting password. Required when reporting is on.
FLUXER_NCMEC_REPORTER_EMAIL
Section titled “FLUXER_NCMEC_REPORTER_EMAIL”Default empty. The contact address on reports. Required when reporting is on.
FLUXER_CLAMAV_ENABLED
Section titled “FLUXER_CLAMAV_ENABLED”Default false. Upload virus scanning. Compose hardcodes false, 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.
FLUXER_CLAMAV_PORT
Section titled “FLUXER_CLAMAV_PORT”Default 3310. The scanner port. Integer.
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.
FLUXER_API_CONTENT_MODERATION_NSFW_THRESHOLD
Section titled “FLUXER_API_CONTENT_MODERATION_NSFW_THRESHOLD”Default 0.7. The API-side NSFW score cutoff. No range check. Distinct from the Media Proxy threshold, which defaults to 0.85.
FLUXER_RISK_INTEGRATION_ENABLED
Section titled “FLUXER_RISK_INTEGRATION_ENABLED”Default false. IP intelligence. Needs an ipinfo key to do anything.
FLUXER_RISK_IPINFO_API_KEY
Section titled “FLUXER_RISK_IPINFO_API_KEY”Default empty. The ipinfo key. Paired with FLUXER_RISK_INTEGRATION_ENABLED.
FLUXER_ACCOUNT_POLICY_DSL
Section titled “FLUXER_ACCOUNT_POLICY_DSL”No default. The account risk policy. JSON. Malformed JSON fails startup, and an unknown key in a well-formed policy surfaces when the policy runs.
FLUXER_RISK_TOR_BLOCK_ALL_RELAYS
Section titled “FLUXER_RISK_TOR_BLOCK_ALL_RELAYS”Default false. Whether every Tor relay is blocked. Covers entry and middle relays as well as exit nodes.
FLUXER_RISK_TOR_REVERSE_DNS_HEURISTIC
Section titled “FLUXER_RISK_TOR_REVERSE_DNS_HEURISTIC”Default false. Reverse DNS Tor detection. Adds a lookup to the request path.
FLUXER_RISK_TOR_REVERSE_DNS_TIMEOUT_MS
Section titled “FLUXER_RISK_TOR_REVERSE_DNS_TIMEOUT_MS”Default 750. The lookup timeout. Milliseconds.
FLUXER_ABUSE_INBOUND_PHONE_COUNTRY_CODES
Section titled “FLUXER_ABUSE_INBOUND_PHONE_COUNTRY_CODES”Default empty. Allowed inbound phone countries. Comma separated, passed through unvalidated.
FLUXER_ABUSE_PHONE_INBOUND_REQUIRED_PREFIXES
Section titled “FLUXER_ABUSE_PHONE_INBOUND_REQUIRED_PREFIXES”Default empty. Required inbound prefixes. Comma separated.
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ENABLED
Section titled “FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ENABLED”Default false. Direct contact spam detection.
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_COUNTRY_CODES
Section titled “FLUXER_ABUSE_DIRECT_CONTACT_SPAM_COUNTRY_CODES”Default empty. Countries the rule applies to. Comma separated, unvalidated.
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_DISTINCT_TARGET_THRESHOLD
Section titled “FLUXER_ABUSE_DIRECT_CONTACT_SPAM_DISTINCT_TARGET_THRESHOLD”Default 25. Distinct targets before the rule fires. Integer.
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_TARGET_WINDOW_MS
Section titled “FLUXER_ABUSE_DIRECT_CONTACT_SPAM_TARGET_WINDOW_MS”Default 7200000. The observation window. Milliseconds, two hours by default.
FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ACTION
Section titled “FLUXER_ABUSE_DIRECT_CONTACT_SPAM_ACTION”Default flag_spammer. What happens when it fires. flag_spammer or suppress_delivery. Anything else fails startup.
FLUXER_BLOCKLIST_FEEDS_ENABLED
Section titled “FLUXER_BLOCKLIST_FEEDS_ENABLED”Defaults to the inverse of FLUXER_SELF_HOSTED. External blocklist feeds. Off by default on a self-hosted instance.
A second family, unrelated to the rules above, tunes the IP auto-banner: FLUXER_ABUSE_WINDOW_MS, the FLUXER_ABUSE_THRESHOLD_ names, the FLUXER_ABUSE_TOKEN_DIVERSITY_ names, FLUXER_ABUSE_BAN_TTL_SEC, FLUXER_ABUSE_BATCH_FLUSH_MS, FLUXER_ABUSE_MAX_BATCH_TICKS, FLUXER_ABUSE_MAX_NEW_TOKENS_PER_TICK, FLUXER_ABUSE_MAX_TRACKED_IPS, FLUXER_ABUSE_MAX_TOKEN_HASHES_PER_IP, FLUXER_ABUSE_MIN_SCORE_FOR_LOOKUP, FLUXER_ABUSE_MIN_TOKENS_FOR_LOOKUP, and FLUXER_ABUSE_REQUIRED_SCORE_WINDOWS_FOR_AUTO_BAN. All are read directly from the environment, none are in .env.example or the Compose file, and a non-finite value or one at or below zero falls back to the default.
Other names in that family limit how often the auto-banner buys an IP classification from ipinfo. A shared claim lets one replica do the lookup for the others. With the claim off, every API replica looks up the same attacking IP at the same moment, so one IP costs one lookup per replica.
FLUXER_ABUSE_IP_CLASS_CLAIM_ENABLEDdefaults to1and gates the shared claim. It is read as a string, and only0turns the claim off.FLUXER_ABUSE_IP_CLASS_CLAIM_TTL_SECdefaults to15seconds and sets how long a replica holds that claim.FLUXER_ABUSE_IP_CLASS_PENDING_TTL_MSdefaults to20000milliseconds and sets how long a replica that lost the claim waits before it tries again.FLUXER_ABUSE_IP_CLASS_NEGATIVE_TTL_MSdefaults to300000milliseconds and sets how long a failed classification is remembered.FLUXER_ABUSE_IP_CLASS_HINT_TTL_MSdefaults to600000milliseconds and sets how long a class sent by another replica stays usable.
For these numeric settings, a non-finite value or one at or below zero falls back to the default.
A FLUXER_IPINFO_BUDGET_ family caps what the instance spends at ipinfo. FLUXER_IPINFO_BUDGET_ENABLED defaults to 1, and 0 turns off all shedding. FLUXER_IPINFO_BUDGET_MONTHLY_MAX defaults to 140000 and is the ceiling for one UTC calendar month. Lookups run at the priorities below, each with a share of that ceiling and a token bucket for bursts refilled once a minute.
- Admin IP bans and scheduled deletion checks are critical. They reach the full ceiling, with a burst of
60fromFLUXER_IPINFO_BUDGET_CRITICAL_BURSTrefilled at60a minute byFLUXER_IPINFO_BUDGET_CRITICAL_REFILL_PER_MIN. - Registration risk is standard. It stops at
FLUXER_IPINFO_BUDGET_STANDARD_MONTHLY_PCTpercent of the ceiling, default90, with a burst of240fromFLUXER_IPINFO_BUDGET_STANDARD_BURSTrefilled at120a minute byFLUXER_IPINFO_BUDGET_STANDARD_REFILL_PER_MIN. - The IP auto-banner is background. It stops at
FLUXER_IPINFO_BUDGET_BACKGROUND_MONTHLY_PCTpercent, default60, with a burst of120fromFLUXER_IPINFO_BUDGET_BACKGROUND_BURSTrefilled at30a minute byFLUXER_IPINFO_BUDGET_BACKGROUND_REFILL_PER_MIN.
Background lookups stop first and critical lookups stop last. A denied lookup returns an unavailable result. If the key-value store fails, lookups remain allowed at every priority, so an outage can increase usage beyond these budgets.
Local MaxMind data can reduce registration-risk lookups. Set FLUXER_RISK_IPINFO_PRESCREEN_ENABLED to 1 or true, in any letter case, and fill FLUXER_RISK_IPINFO_PRESCREEN_ALLOW_ASNS with comma-separated ASN numbers. Non-numeric entries are dropped. An IP skips ipinfo only when MaxMind supplies its country and an allowed ASN, and the organisation is not a commercial privacy provider, education network or cellular network.
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.
- 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. Effective limits combine saved settings, deployment defaults and premium policy. 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.
FLUXER_GATEWAY_HTTP_RPC_MAX_CONCURRENCY
Section titled “FLUXER_GATEWAY_HTTP_RPC_MAX_CONCURRENCY”Default 512. Gateway HTTP RPC concurrency.
FLUXER_GATEWAY_NATS_RPC_MAX_HANDLERS
Section titled “FLUXER_GATEWAY_NATS_RPC_MAX_HANDLERS”Default 512. Gateway NATS handler count.
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.
FLUXER_RELAX_REGISTRATION_RATE_LIMITS
Section titled “FLUXER_RELAX_REGISTRATION_RATE_LIMITS”Default false. Loosens registration rate limits. Reaches production containers if set.
Telemetry, logging and build metadata
Section titled “Telemetry, 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.
RUST_LOG
Section titled “RUST_LOG”Default info. The Rust log filter. Read by media-proxy, app-proxy, admin, and the internal services. Not in .env.example or the Compose file.
FLUXER_GATEWAY_LOGGER_LEVEL
Section titled “FLUXER_GATEWAY_LOGGER_LEVEL”Default info. The Gateway log level. Compose sets info.
LOGGER_LEVEL
Section titled “LOGGER_LEVEL”Falls back to FLUXER_GATEWAY_LOGGER_LEVEL. The Gateway log level at runtime. Overrides the prefixed name.
BUILD_VERSION
Section titled “BUILD_VERSION”Default dev. The reported build. Baked into the images. 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.
FLUXER_TELEMETRY_ENABLED
Section titled “FLUXER_TELEMETRY_ENABLED”Defaults to false at the Gateway’s environment layer. Gateway telemetry. Read only by the Gateway, and in neither .env.example nor the Compose file.
HOSTNAME
Section titled “HOSTNAME”Set by Docker. Node identity in metrics and logs. Also used by the IP auto-banner.
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, billing and discovery controllers, and turns blocklist feeds off.
/_metrics on api, media-proxy, and gateway, 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.
FLUXER_DISCOVERY_MIN_MEMBER_COUNT
Section titled “FLUXER_DISCOVERY_MIN_MEMBER_COUNT”Default 1. Minimum members for discovery eligibility. Integer.
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.
FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED
Section titled “FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED”Default false. Presigned attachment uploads. Compose sets true.
FLUXER_API_PRESIGNED_DOWNLOADS_ENABLED
Section titled “FLUXER_API_PRESIGNED_DOWNLOADS_ENABLED”Default false. Presigned downloads. Applies to the downloads bucket.
FLUXER_API_PRESIGNED_HARVEST_DOWNLOADS_ENABLED
Section titled “FLUXER_API_PRESIGNED_HARVEST_DOWNLOADS_ENABLED”Default true. Presigned harvest downloads. Applies to the harvests bucket.
FLUXER_API_EMBEDS_OEMBED_HTML_ENABLED
Section titled “FLUXER_API_EMBEDS_OEMBED_HTML_ENABLED”Default false. oEmbed HTML in embeds.
FLUXER_API_EMBEDS_OEMBED_HTML_ALLOW_UNTRUSTED_ON_SELF_HOSTED
Section titled “FLUXER_API_EMBEDS_OEMBED_HTML_ALLOW_UNTRUSTED_ON_SELF_HOSTED”Default false. Untrusted oEmbed HTML on a self-hosted instance. Security relevant. Leave it off.
FLUXER_API_EMBEDS_OEMBED_HTML_ALLOWED_HOSTS
Section titled “FLUXER_API_EMBEDS_OEMBED_HTML_ALLOWED_HOSTS”Default empty. Hosts allowed to supply oEmbed HTML. Comma separated.
FLUXER_API_EMBEDS_CACHE_DEFAULT_TTL_SECONDS
Section titled “FLUXER_API_EMBEDS_CACHE_DEFAULT_TTL_SECONDS”Default 86400. Default embed cache lifetime. Seconds.
FLUXER_API_EMBEDS_CACHE_MAX_TTL_SECONDS
Section titled “FLUXER_API_EMBEDS_CACHE_MAX_TTL_SECONDS”Default 604800. Maximum embed cache lifetime. Seconds.
FLUXER_API_EMBEDS_CACHE_MIN_TTL_SECONDS
Section titled “FLUXER_API_EMBEDS_CACHE_MIN_TTL_SECONDS”Default 300. Minimum embed cache lifetime. Seconds.
FLUXER_API_EMBEDS_CACHE_RESPECT_REMOTE_TTL
Section titled “FLUXER_API_EMBEDS_CACHE_RESPECT_REMOTE_TTL”Default true. Whether a remote cache header is honoured. Clamped by FLUXER_API_EMBEDS_CACHE_MIN_TTL_SECONDS and FLUXER_API_EMBEDS_CACHE_MAX_TTL_SECONDS.
FLUXER_API_UNFURL_IGNORED_HOSTS
Section titled “FLUXER_API_UNFURL_IGNORED_HOSTS”Default empty. Hosts never unfurled. Comma separated, unvalidated.
FLUXER_API_DESKTOP_GITHUB_REDIRECT_COUNTRIES
Section titled “FLUXER_API_DESKTOP_GITHUB_REDIRECT_COUNTRIES”Default empty. Countries redirected to GitHub for desktop downloads. Each entry must be two uppercase letters or the API fails at boot.
FLUXER_TEST_MODE_ENABLED
Section titled “FLUXER_TEST_MODE_ENABLED”Default false. Test mode. Collapses the deletion grace period. Reaches production containers if set.
FLUXER_TEST_HARNESS_TOKEN
Section titled “FLUXER_TEST_HARNESS_TOKEN”No default. The test harness credential. Reaches production containers if set.
FLUXER_VALIDATE_RESPONSES
Section titled “FLUXER_VALIDATE_RESPONSES”Defaults to on outside production. Response schema validation. Costs latency when forced on.
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.
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.
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.
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.
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.
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.
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. app-proxy also accepts MAXMIND_DB_PATH. The API also reads an ASN database. On a filesystem path it looks for GeoLite2-ASN.mmdb in the same directory. On an s3:// URL it needs an asn_key query parameter, and takes an optional absolute asn_download_path next to it. A missing ASN database is not an error, and the API falls back to ipinfo for ASN data.
Instance identity and branding
Section titled “Instance identity and branding”None of these are in .env.example or in docker-compose.yml. Set branding from the admin dashboard instead. All are optional.
FLUXER_APP_PRODUCT_NAME
Section titled “FLUXER_APP_PRODUCT_NAME”Default Fluxer. The product name clients display. Also settable in the admin dashboard, which wins.
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.
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.
FLUXER_APP_LOGO_URL
Section titled “FLUXER_APP_LOGO_URL”Default empty. The logo. Its origin is added to the CSP by app-proxy.
FLUXER_APP_WORDMARK_URL
Section titled “FLUXER_APP_WORDMARK_URL”Default empty. The wordmark. Its origin is added to the CSP by app-proxy.
FLUXER_APP_FAVICON_URL
Section titled “FLUXER_APP_FAVICON_URL”Default empty. The favicon. Its origin is added to the CSP by app-proxy.
FLUXER_APP_THEME_COLOR
Section titled “FLUXER_APP_THEME_COLOR”Default empty. The theme colour.
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.
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.
FLUXER_VISIONARIES_GUILD_ID
Section titled “FLUXER_VISIONARIES_GUILD_ID”Default empty. The guild that grants the visionary role. Snowflake.
FLUXER_VISIONARIES_GUILD_VISIONARY_ROLE_ID
Section titled “FLUXER_VISIONARIES_GUILD_VISIONARY_ROLE_ID”Default empty. The role granted there. Snowflake.
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 and the edge proxies to it.
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.
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.
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.
FLUXER_API_WORKER_LANE
Section titled “FLUXER_API_WORKER_LANE”No default. Which lane, under single_lane. realtime, unfurl, lifecycle, or batch.
FLUXER_API_WORKER_TASK
Section titled “FLUXER_API_WORKER_TASK”No default. Which task, under single_task. Must name a known task.
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. 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.
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, or upload. Anything else is a startup error. Compose sets upload.
FLUXER_MEDIA_PROXY_HOST
Section titled “FLUXER_MEDIA_PROXY_HOST”Default 0.0.0.0. The bind address. Also settable with --bind-host.
FLUXER_MEDIA_PROXY_PORT
Section titled “FLUXER_MEDIA_PROXY_PORT”Default 8080. The listen port. Also settable with --port.
FLUXER_MEDIA_PROXY_READ_ONLY
Section titled “FLUXER_MEDIA_PROXY_READ_ONLY”Default false. Refuses writes. --read-only can force it on.
FLUXER_MEDIA_PROXY_STORAGE_BACKEND
Section titled “FLUXER_MEDIA_PROXY_STORAGE_BACKEND”Default local. Where objects live. local or s3. Compose sets s3.
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.
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.
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.
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.
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.
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.
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. Must be writable.
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.
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. 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.
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.
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.
FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_BYTES
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_CACHE_BYTES”Default 268435456. Transform cache size. Accepts 0 to 4 GiB.
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.
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.
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.
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.
FLUXER_MEDIA_PROXY_TRANSFORM_TIMEOUT_MS
Section titled “FLUXER_MEDIA_PROXY_TRANSFORM_TIMEOUT_MS”Default 15000. Per-transform timeout. Accepts 1000 to 120000.
FLUXER_MEDIA_PROXY_MAX_ENCODE_FRAMES
Section titled “FLUXER_MEDIA_PROXY_MAX_ENCODE_FRAMES”Default 20000. Animation frame ceiling. Accepts 1 to 100000.
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.
FLUXER_NSFW_SERVICE_ENDPOINT
Section titled “FLUXER_NSFW_SERVICE_ENDPOINT”Default empty. An external NSFW classifier. No such service ships with the stack.
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. Distinct from the API threshold.
media-proxy range-checks these values at startup and then reads them nowhere: FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_BYTES and FLUXER_MEDIA_PROXY_UPLOAD_RELAY_BUFFERED_RETRY_TOTAL_BYTES. A bad value still fails the boot.
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.
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.
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 builds it from the public origin.
FLUXER_GATEWAY_STATIC_CDN_ENDPOINT
Section titled “FLUXER_GATEWAY_STATIC_CDN_ENDPOINT”Default http://localhost:8088. The public static origin used in payloads. Compose builds it from the public origin.
FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_ENTRIES
Section titled “FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_ENTRIES”Default 128. Presence push buffer depth.
FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_BYTES
Section titled “FLUXER_GATEWAY_PRESENCE_PUSH_BUFFER_MAX_BYTES”Default 1048576. Presence push buffer size.
FLUXER_GATEWAY_SHUTDOWN_DRAIN_WAIT_MS
Section titled “FLUXER_GATEWAY_SHUTDOWN_DRAIN_WAIT_MS”Default 5000. Milliseconds. The Gateway parses it into its configuration at startup, and nothing reads it after that, so it controls no drain.
FLUXER_GATEWAY_HTTP_FAILURE_THRESHOLD
Section titled “FLUXER_GATEWAY_HTTP_FAILURE_THRESHOLD”Default 6. Failures before the API circuit opens. Integer.
FLUXER_GATEWAY_HTTP_RECOVERY_TIMEOUT_MS
Section titled “FLUXER_GATEWAY_HTTP_RECOVERY_TIMEOUT_MS”Default 15000. How long the circuit stays open. Milliseconds.
FLUXER_GATEWAY_CLUSTER_ENABLED
Section titled “FLUXER_GATEWAY_CLUSTER_ENABLED”Default false. BEAM clustering. Single-node by default.
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 fluxer_gateway@127.0.0.1. The BEAM node name. Must be resolvable by peers when clustering.
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 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.
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.
FLUXER_ERLANG_SCHEDULERS_MIN
Section titled “FLUXER_ERLANG_SCHEDULERS_MIN”Default 2. The floor of the scheduler clamp. Compose forwards it to gateway.
FLUXER_ERLANG_SCHEDULERS_MAX
Section titled “FLUXER_ERLANG_SCHEDULERS_MAX”Default 16. The ceiling of the scheduler clamp. Compose forwards it 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 on the gateway service in docker-compose.yml, 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, so any of them works once it is in the gateway container environment. The bundled Compose file passes only the names it lists, so add any other name through 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. Compose sets it explicitly.
FLUXER_APP_PROXY_PORT
Section titled “FLUXER_APP_PROXY_PORT”Default 8080. The listen port. Compose sets it explicitly.
FLUXER_STATIC_DIR
Section titled “FLUXER_STATIC_DIR”Default ./static. Where the client’s SPA bundle lives, unrelated to static-proxy.
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.
DISCOVERY_UPSTREAM_URL
Section titled “DISCOVERY_UPSTREAM_URL”Default http://localhost:8088/api/.well-known/fluxer. Where the bootstrap discovery document is fetched. 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. Unprefixed name.
PUBLIC_BOOTSTRAP_API_ENDPOINT
Section titled “PUBLIC_BOOTSTRAP_API_ENDPOINT”Default /api. The API path put in the page bootstrap.
PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT
Section titled “PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT”No default. The absolute API URL in the bootstrap. Compose builds it from the public origin.
FLUXER_APP_PROXY_TIME_FREEZE_ENABLED
Section titled “FLUXER_APP_PROXY_TIME_FREEZE_ENABLED”Defaults to the inverse of FLUXER_SELF_HOSTED. A frozen asset snapshot. Also compiled out of the self-hosted image.
Admin settings
Section titled “Admin settings”admin serves the dashboard at /admin. All are optional.
FLUXER_ADMIN_HOST
Section titled “FLUXER_ADMIN_HOST”Default 0.0.0.0. The bind address. Compose sets it explicitly.
FLUXER_ADMIN_PORT
Section titled “FLUXER_ADMIN_PORT”Default 3020. The listen port. The image sets 8080 and Compose sets 8080.
FLUXER_ADMIN_BASE_PATH
Section titled “FLUXER_ADMIN_BASE_PATH”Defaults to empty in Rust, /admin in Node. The path the dashboard is mounted under. Normalised to a leading slash with no trailing slash. The service serves at root and re-prefixes every emitted URL with this.
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. The API does not read this name.
FLUXER_RELEASE_CHANNEL
Section titled “FLUXER_RELEASE_CHANNEL”Default stable. The reported channel. RELEASE_CHANNEL is preferred over it.
FLUXER_BUILD_VERSION
Section titled “FLUXER_BUILD_VERSION”Defaults to the crate version. The reported build. BUILD_VERSION is preferred over it.
Content Security Policy
Section titled “Content Security Policy”app-proxy builds a per-request nonce-based policy for the client HTML and the assets it serves. 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 reads the discovery document and adds the static CDN endpoint, the media endpoint, and the origins of the configured branding images. A stack on one hostname therefore needs no extra sources. 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. The overlay overwrites it with :8080.
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
Section titled “FLUXER_EDGE_TRUSTED_PROXIES”Read by the edge container only. It takes 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.
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 AWS_ pair.
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.
Keys Compose does not forward
Section titled “Keys Compose does not forward”.env.example lists the variables forwarded by docker-compose.yml. Add any setting below through a Compose override file that puts it in the relevant service’s environment.
FLUXER_AUTH_BLUESKY_ and the names under it
Section titled “FLUXER_AUTH_BLUESKY_ and the names under it”See Bluesky connections for configuration.
FLUXER_PUSH_APNS_ and FLUXER_PUSH_FCM_
Section titled “FLUXER_PUSH_APNS_ and FLUXER_PUSH_FCM_”Mobile push cannot be configured at all from the example.
RUST_LOG, LOG_LEVEL and LOGGER_LEVEL
Section titled “RUST_LOG, LOG_LEVEL and LOGGER_LEVEL”The only way to change log verbosity.
FLUXER_APP_PRODUCT_NAME and the branding URLs
Section titled “FLUXER_APP_PRODUCT_NAME and the branding URLs”Branding is otherwise admin-dashboard only.
FLUXER_INSTANCE_SETUP_CONFIGURED
Section titled “FLUXER_INSTANCE_SETUP_CONFIGURED”Supplies the initial setup state on a self-hosted instance, until the first write of the stored app_public_config row takes over.
Every FLUXER_ABUSE_ auto-banner name, every FLUXER_IPINFO_ budget name, and every Media Proxy performance knob
Section titled “Every FLUXER_ABUSE_ auto-banner name, every FLUXER_IPINFO_ budget name, and every Media Proxy performance knob”No example coverage at all.
Runtime settings in the admin dashboard
Section titled “Runtime settings in the admin dashboard”The admin dashboard at /admin 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, and the setup state in the instance discovery document.
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, 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, the CAPTCHA provider and its keys, email delivery with an SMTP connection test, and Bluesky OAuth.
Instance Config, Media Expiry
Section titled “Instance Config, Media Expiry”Size-based attachment lifetimes. The built-in default is on.
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.10-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 |
| 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:8.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.12 | The search index |
| seaweedfs | chrislusf/seaweedfs:4.34 | S3-compatible object storage |
| seaweedfs-init | chrislusf/seaweedfs:4.34 | Creates the buckets and the S3 identity, then exits |
| livekit | livekit/livekit-server:v1.12.0 | Voice and video |
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.
api is the one service an operator configures directly, through the shared environment block. worker, gateway, app-proxy, and media-proxy are touched rarely, worker for lane concurrency, app-proxy for CSP extras, and media-proxy for transform limits. The edge takes only the FLUXER_EDGE_ variables, postgres only the password, meilisearch only the master key, valkey only the FLUXER_VALKEY_ tuning values, and livekit only the key pair and the port variables. static-proxy reads no environment variables, and the remaining services need none.
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 16.75 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. The 256mb 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 768mb. The ceiling for meilisearch. Must stay well above FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY, which bounds the indexer alone.
FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY
Section titled “FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY”Default 384mb. Becomes MEILI_MAX_INDEXING_MEMORY. The memory the indexer may use for one batch. Lower it whenever you lower FLUXER_MEILISEARCH_MEMORY_LIMIT.
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_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_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 holds the discovery cache 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.
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.
The remaining Postgres settings are fixed on the command line, with no variable of their own: min_wal_size=512MB, max_wal_size=2GB, wal_buffers=16MB, and shm_size: 256mb on the service itself.
The bundled Valkey uses persistent storage. These settings control its memory limit and behaviour when full.
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.
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=512mbFLUXER_MEILISEARCH_MAX_INDEXING_MEMORY=256mbFLUXER_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=384mbFLUXER_MEILISEARCH_MAX_INDEXING_MEMORY=128mbFLUXER_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.
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.
None of the variables on this page change that routing. The Caddyfile is a bind mount, so an edit to it takes docker compose restart edge.
CORS origins are exactly the app and marketing endpoints. 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 | Deletion 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 | No |
Losing valkey-data can delay scheduled account and bulk-message deletions while their queues are rebuilt. Pending asset deletions and cache purges can be lost, so do not treat this volume as disposable cache.
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.
| Bucket | Holds |
|---|---|
| fluxer | Avatars, guild and entity assets, themes, entrance sounds, memes, and processed attachments |
| fluxer-uploads | Raw attachment uploads, before processing |
| fluxer-downloads | Desktop client build artefacts |
| fluxer-reports | Abuse report evidence, and NCMEC payloads where that integration is on |
| fluxer-harvests | User and guild data archives |
FLUXER_S3_BUCKET_STATIC names an optional bucket. The shipped stack does not use or create it.