# Every variable docker-compose.yml reads from this file is named here: # uncommented when it has no default, commented with its default when it has one. # A name absent from this file is one Compose does not forward, and it reaches a # service only through a Compose override file that adds it to that service's # environment. packages/config/src/__tests__/DeployEnvCoverage.test.ts fails when # a Compose edit forgets the matching line here. FLUXER_DOMAIN=chat.example.com FLUXER_PUBLIC_SCHEME=https FLUXER_PUBLIC_PORT=443 # How browsers reach this instance. # # Default: Fluxer binds 80 and 443 and gets its own Let's Encrypt certificate. # Point DNS at this host and there is nothing else to configure. # # Behind your own reverse proxy (nginx, Traefik, HAProxy, Cloudflare Tunnel, # another Caddy): uncomment COMPOSE_FILE below. Fluxer then serves plain HTTP on # 127.0.0.1:8080 instead, and your proxy forwards everything to it. Keep # FLUXER_PUBLIC_SCHEME and FLUXER_PUBLIC_PORT describing the PUBLIC address your # proxy serves, not this local port. #COMPOSE_FILE=docker-compose.yml:docker-compose.proxy.yml # Where the plain-HTTP port binds when the proxy overlay is in use. Leave it on # loopback when the proxy runs on this host. Use 0.0.0.0:8080 only when the proxy # is on another machine, and firewall the port to that machine. #FLUXER_EDGE_BIND=127.0.0.1:8080 # Which upstream hops may set X-Forwarded-For. Fluxer rewrites the header from # this to the real client address, so IP bans, rate limits and abuse detection # see the caller rather than the proxy. The default covers proxies on private or # loopback addresses, which is every same-host setup. Set it to your proxy's # address if it reaches Fluxer from a public IP. #FLUXER_EDGE_TRUSTED_PROXIES=private_ranges # The public origin browsers use, without a trailing slash. Derived from the three # values above and correct for the usual https-on-443 setup, so leave it alone # unless you serve Fluxer on a non-default port, where the port must appear here. #FLUXER_PUBLIC_ORIGIN=https://chat.example.com # Overrides the address Fluxer's edge listens on. Honoured in the default mode # only: docker-compose.proxy.yml sets the literal :8080 and Compose lets the last # file win, so a value here is discarded under the proxy overlay with no warning. # Set it only for an unusual default-mode layout, such as serving several # hostnames or binding a non-default TLS port. #FLUXER_EDGE_SITE_ADDRESS=chat.example.com # The old name for the value above. It is read only when # FLUXER_EDGE_SITE_ADDRESS is unset, so an existing .env keeps the listener # it already had. Rename it to FLUXER_EDGE_SITE_ADDRESS at your convenience. #FLUXER_CADDY_SITE_ADDRESS= # FLUXER_PUBLIC_ORIGIN is the origin browsers see. It must carry the port # whenever FLUXER_PUBLIC_PORT is not the default for its scheme, because an # origin written with a default port never matches a browser Origin header. # Serving on any other port means setting all three, plus the published port # below, and pointing FLUXER_EDGE_SITE_ADDRESS at the same scheme and host. # Compose expands this file from top to bottom, so FLUXER_PUBLIC_ORIGIN has to # stay below the two values it reads. Above them it silently expands to a bare # host with a trailing colon. #FLUXER_PUBLIC_SCHEME=http #FLUXER_PUBLIC_PORT=19080 #FLUXER_PUBLIC_ORIGIN=${FLUXER_PUBLIC_SCHEME}://${FLUXER_DOMAIN}:${FLUXER_PUBLIC_PORT} #FLUXER_HTTP_PORT=19080 # Ports Caddy publishes on the host. Caddy still listens on 80 and 443 inside # the container, so change only these when something else already owns the # standard ports or another proxy sits in front. Both take an optional bind # address in front of the port, and 127.0.0.1 keeps the publish off every # public interface. FLUXER_HTTPS_PORT moves the TCP and the UDP publish # together, because HTTP/3 needs both on the same port. #FLUXER_HTTP_PORT=80 #FLUXER_HTTPS_PORT=443 #FLUXER_HTTP_PORT=127.0.0.1:80 #FLUXER_HTTPS_PORT=127.0.0.1:443 # A tunnel or another proxy in front of the stack needs no HTTPS publish at all. # tunnel.compose.yml ships beside this file and replaces Caddy's published ports # with a single loopback HTTP publish, so nothing binds 443. FLUXER_HTTP_PORT # still moves that one publish. Set the line below and plain docker compose # commands pick the file up, or add it to your own -f flags if you pass any. The # file uses the !override tag, which needs Compose 2.24.4 or newer. #COMPOSE_FILE=docker-compose.yml:tunnel.compose.yml FLUXER_REGISTRY_OWNER=fluxerapp FLUXER_REGISTRY=ghcr.io/${FLUXER_REGISTRY_OWNER} FLUXER_IMAGE_TAG=v1 POSTGRES_PASSWORD=CHANGE_ME MEILI_MASTER_KEY=CHANGE_ME FLUXER_S3_ACCESS_KEY=fluxer FLUXER_S3_SECRET_KEY=CHANGE_ME FLUXER_SUDO_MODE_SECRET=CHANGE_ME FLUXER_CONNECTION_INITIATION_SECRET=CHANGE_ME FLUXER_GATEWAY_RPC_AUTH_TOKEN=CHANGE_ME FLUXER_ERLANG_COOKIE=CHANGE_ME FLUXER_MEDIA_PROXY_SECRET_KEY=CHANGE_ME FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=CHANGE_ME FLUXER_ADMIN_SECRET_KEY_BASE=CHANGE_ME FLUXER_ADMIN_OAUTH_CLIENT_SECRET=CHANGE_ME FLUXER_VAPID_PUBLIC_KEY=CHANGE_ME FLUXER_VAPID_PRIVATE_KEY=CHANGE_ME # The VAPID contact address defaults to admin@ followed by FLUXER_DOMAIN. Set it # only if that mailbox does not exist. #FLUXER_VAPID_EMAIL=admin@chat.example.com # Passkeys follow FLUXER_DOMAIN by default. Set these only if browsers reach the # instance on a different host, and note that changing FLUXER_PASSKEY_RP_ID # invalidates every passkey already registered against the old value. #FLUXER_PASSKEY_RP_ID=chat.example.com #FLUXER_PASSKEY_RP_NAME=Fluxer #FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS=https://chat.example.com #FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS=http://chat.example.com:19080 # Extra Content-Security-Policy sources, appended to the built-in ones. Set these # only when a browser must reach an origin the defaults do not cover, such as a # voice server hosted on a domain other than FLUXER_DOMAIN. Separate several # sources with spaces or commas. Every one of them is empty by default, and the # three carrying a value below are illustrations, not defaults. #FLUXER_CSP_EXTRA_DEFAULT_SRC= #FLUXER_CSP_EXTRA_CONNECT_SRC=wss://livekit.example.com:7881 #FLUXER_CSP_EXTRA_IMG_SRC=https://cdn.example.com #FLUXER_CSP_EXTRA_MEDIA_SRC= #FLUXER_CSP_EXTRA_FONT_SRC= #FLUXER_CSP_EXTRA_SCRIPT_SRC=https://analytics.example.com #FLUXER_CSP_EXTRA_STYLE_SRC= #FLUXER_CSP_EXTRA_FRAME_SRC= #FLUXER_CSP_EXTRA_WORKER_SRC= #FLUXER_CSP_EXTRA_MANIFEST_SRC= # One report-uri for Content-Security-Policy violation reports. Empty leaves the # directive off the header. #FLUXER_CSP_REPORT_URI= # Allow the SSO identity provider to resolve to a private or internal address. # Off by default: the API refuses to call non-public addresses so a misconfigured # provider URL cannot be used to reach internal services. Turn it on only when the # provider genuinely lives on your own network, such as split-horizon DNS or a LAN # identity provider, and only when you trust everyone who can configure SSO. #FLUXER_SSO_ALLOW_PRIVATE_ADDRESSES=true # Both reach LiveKit as LIVEKIT_KEYS and the webhook signing key, and the API as # FLUXER_LIVEKIT_API_KEY and FLUXER_LIVEKIT_API_SECRET. Change them together. LIVEKIT_API_KEY=fluxer LIVEKIT_API_SECRET=CHANGE_ME # The URL browsers use for voice signalling. Derived from FLUXER_PUBLIC_SCHEME, # FLUXER_DOMAIN and FLUXER_PUBLIC_PORT as wss://host[:port]/livekit when empty. # Set it only when LiveKit is served from another host. #FLUXER_LIVEKIT_URL= # Media ports. LiveKit advertises these in ICE candidates, so the host must # forward the same numbers. #FLUXER_LIVEKIT_TCP_PORT=7881 #FLUXER_LIVEKIT_UDP_PORT=7882 FLUXER_KLIPY_API_KEY= FLUXER_EMAIL_ENABLED=false FLUXER_EMAIL_PROVIDER=none FLUXER_EMAIL_FROM_EMAIL=noreply@example.com FLUXER_EMAIL_FROM_NAME=Fluxer FLUXER_EMAIL_APP_BASE_URL= FLUXER_EMAIL_SMTP_HOST= FLUXER_EMAIL_SMTP_PORT=587 FLUXER_EMAIL_SMTP_USERNAME= FLUXER_EMAIL_SMTP_PASSWORD= FLUXER_EMAIL_SMTP_SECURE=true FLUXER_CAPTCHA_ENABLED=false FLUXER_CAPTCHA_PROVIDER=none FLUXER_CAPTCHA_HCAPTCHA_SITE_KEY= FLUXER_CAPTCHA_HCAPTCHA_SECRET_KEY= FLUXER_CAPTCHA_TURNSTILE_SITE_KEY= FLUXER_CAPTCHA_TURNSTILE_SECRET_KEY= FLUXER_DISCOVERY_ENABLED=true # Container memory. The 25 limits sum to 16.75 GiB, which is a sum of ceilings and # not an allocation, so the defaults fit a host with 8 GB and are sized for 16 GB. # The four reservations are cgroup memory.low, which biases the kernel away from # reclaiming from the services whose death takes the whole instance down. They do # not reserve anything. Lower the limits on a smaller host. #FLUXER_CADDY_MEMORY_LIMIT=256mb #FLUXER_POSTGRES_MEMORY_LIMIT=5gb #FLUXER_POSTGRES_MEMORY_RESERVATION=3gb #FLUXER_VALKEY_MEMORY_LIMIT=256mb #FLUXER_NATS_MEMORY_LIMIT=256mb #FLUXER_MEILISEARCH_MEMORY_LIMIT=768mb #FLUXER_SEAWEEDFS_MEMORY_LIMIT=512mb #FLUXER_SEAWEEDFS_INIT_MEMORY_LIMIT=128mb #FLUXER_LIVEKIT_MEMORY_LIMIT=512mb #FLUXER_API_MEMORY_LIMIT=2560mb #FLUXER_API_MEMORY_RESERVATION=1gb #FLUXER_WORKER_MEMORY_LIMIT=2560mb #FLUXER_WORKER_MEMORY_RESERVATION=1gb #FLUXER_GATEWAY_MEMORY_LIMIT=1gb #FLUXER_GATEWAY_MEMORY_RESERVATION=384mb #FLUXER_MEDIA_PROXY_MEMORY_LIMIT=512mb #FLUXER_STATIC_PROXY_MEMORY_LIMIT=256mb #FLUXER_APP_PROXY_MEMORY_LIMIT=256mb #FLUXER_SNOWFLAKES_MEMORY_LIMIT=128mb #FLUXER_SNOWFLAKES_SHARD_MEMORY_LIMIT=256mb #FLUXER_USERS_MEMORY_LIMIT=128mb #FLUXER_USERS_SHARD_MEMORY_LIMIT=256mb #FLUXER_GIFS_MEMORY_LIMIT=128mb #FLUXER_GIFS_SHARD_MEMORY_LIMIT=256mb #FLUXER_MESSAGES_MEMORY_LIMIT=128mb #FLUXER_MESSAGES_SHARD_MEMORY_LIMIT=256mb #FLUXER_UNFURL_MEMORY_LIMIT=128mb #FLUXER_UNFURL_SHARD_MEMORY_LIMIT=256mb #FLUXER_ADMIN_MEMORY_LIMIT=256mb # Meilisearch indexing memory. Keep it well under FLUXER_MEILISEARCH_MEMORY_LIMIT, # which is the container ceiling the indexer shares with the search process. #FLUXER_MEILISEARCH_MAX_INDEXING_MEMORY=384mb # Node sizes its own heap from the container memory limit by default, at roughly # 55 percent of it, which always leaves room for the buffers and stacks that live # outside the heap. Leave these unset unless you have a reason to pin the value. # Any value set here must stay well below the container limit above: a heap ceiling # above the container limit makes the kernel OOM-kill the container (exit 137, no # diagnostics) instead of Node reporting a JavaScript heap out of memory error. #FLUXER_API_NODE_HEAP_MB=1792 #FLUXER_WORKER_NODE_HEAP_MB=1792 # Bundled Postgres tuning. Keep these consistent with FLUXER_POSTGRES_MEMORY_LIMIT: # budget roughly shared_buffers + (server max_connections x 12 MB) + # (3 x autovacuum_work_mem) + 300 MB for page cache and WAL. Note this is the # server setting, distinct from the per-service FLUXER_POSTGRES_MAX_CONNECTIONS # pool sizes used by the api, worker and shards. #FLUXER_POSTGRES_SERVER_MAX_CONNECTIONS=150 #FLUXER_POSTGRES_SHARED_BUFFERS=512MB #FLUXER_POSTGRES_EFFECTIVE_CACHE_SIZE=2GB #FLUXER_POSTGRES_WORK_MEM=8MB #FLUXER_POSTGRES_MAINTENANCE_WORK_MEM=256MB #FLUXER_POSTGRES_AUTOVACUUM_WORK_MEM=128MB # The bundled Valkey holds durable state as well as cache. The bulk message # deletion queue and the account deletion queue are sorted sets with no expiry, # and nothing else stores the first of the two. It therefore runs with an # append-only file on a named volume and with noeviction, so an over-limit write # fails loudly instead of silently deleting queued work. Distributed locks all # carry a TTL and are not what the durability is for. Only change the policy if # you have moved that durable state elsewhere. #FLUXER_VALKEY_MAXMEMORY=192mb #FLUXER_VALKEY_MAXMEMORY_POLICY=noeviction # The gateway derives its BEAM scheduler count from the container CPU quota, # clamped to this range. The floor matters: a single scheduler lets one blocking # operation stall every websocket on the node. The ceiling stops a large host # from starting far more schedulers than the container can actually use. #FLUXER_ERLANG_SCHEDULERS_MIN=2 #FLUXER_ERLANG_SCHEDULERS_MAX=16 # In-flight request ceiling for the four services Compose forwards it to: the # users and messages routers and their shards. The Rust built-in defaults are 192 # for messages, 320 for snowflakes and 64 elsewhere, and they govern every service # Compose does not forward this to. #FLUXER_SVC_MAX_CONCURRENT_REQUESTS=20 # The api and the Rust services name their fixed Postgres statement shapes so the # server can reuse their plans. Named prepared statements require a session that # outlives the transaction, so set this to false if you put a transaction-pooling # connection pooler such as PgBouncer in front of Postgres. One setting governs # every service. The bundled compose talks to Postgres directly, where naming is # a win and the default is correct. #FLUXER_POSTGRES_PREPARED_STATEMENTS=true # The api bounds how long a client may take to send a request. The header timeout # covers the request line and headers only, while the request timeout covers the # whole exchange, so a slow uploader is bounded by the second value and not by # the first. Raise both if you front large uploads or serve clients on high # latency links. The header timeout is clamped down to the request timeout, so # raising it alone does nothing. Both are milliseconds, between 1000 and 3600000. #FLUXER_API_HEADERS_TIMEOUT_MS=30000 #FLUXER_API_REQUEST_TIMEOUT_MS=120000