Skip to content

Configuration ​

Firemoot is configured entirely by environment variables - there is no config file. Everything has a working default except the credentials you must not ship with defaults, so a bare docker run against a Postgres boots.

A variable that is present but blank counts as absent. That matters for the optional ones: Compose's ${VAR:-} syntax passes an empty string rather than leaving the variable unset, and without that rule an "unset" FIREMOOT_S3_ENDPOINT would look configured and then fail fast on its missing siblings.

HTTP ​

VariableDefaultWhat it does
FIREMOOT_HTTP_HOST0.0.0.0Bind address.
FIREMOOT_HTTP_PORT6668Bind port. Serves the API, /v1/ws, /admin and the health endpoints.

Database ​

VariableDefaultWhat it does
FIREMOOT_DB_HOSTlocalhostPostgres host.
FIREMOOT_DB_PORT5432Postgres port.
FIREMOOT_DB_NAMEfiremootDatabase name.
FIREMOOT_DB_USERfiremootRole to connect as.
FIREMOOT_DB_PASSWORDfiremootPassword. Change it.
FIREMOOT_DB_MAX_CONNECTIONS10Size of the connection pool.
FIREMOOT_DB_SSLMODEpreferJDBC sslmode for the boot-time Flyway connection.

FIREMOOT_DB_SSLMODE exists for one specific failure. prefer (pgjdbc's own default) asks for TLS and falls back to plaintext if the server refuses it - but some proxies accept the SSL request and then break the handshake, which prefer cannot recover from, so the migration step dies on boot. Fly's unmanaged Postgres over flycast behaves this way; set FIREMOOT_DB_SSLMODE=disable there (the traffic is already inside a private network). Anywhere your Postgres speaks TLS properly, leave it alone.

Server credentials ​

VariableDefaultWhat it does
FIREMOOT_API_KEY_IDfiremootThe bootstrap server API key id.
FIREMOOT_API_SECRETdev-secretThe one credential that matters - see below.
FIREMOOT_ADMIN_PASSWORD(unset)Unlocks the admin dashboard.

FIREMOOT_API_SECRET does two jobs: it is the HMAC key your backend signs server requests with, and the HS256 key that signs and verifies end-user JWTs. Rotate it deliberately - doing so invalidates every live user token. Generate one with openssl rand -hex 32 and keep it out of the browser.

The bootstrap key pair is resolved before the database, so it always works even on an empty schema. Additional keys created from the admin dashboard live in the database and take effect without a restart; the bootstrap key is not one of them and cannot be revoked from the UI.

FIREMOOT_ADMIN_PASSWORD has no default, so the dashboard stays locked until you set one. On boot it is hashed with Argon2id into the settings table - setting the variable again on a later boot resets the password.

Media (S3) ​

Media is off until FIREMOOT_S3_ENDPOINT is set; uploads return 501 until then. Once it is set, the rest of the block loads and a half-configured store fails fast at boot rather than at the first upload.

VariableDefaultWhat it does
FIREMOOT_S3_ENDPOINT(unset)The S3 endpoint. Setting it enables media.
FIREMOOT_S3_BUCKETfiremootBucket name. Create it yourself; Firemoot never calls vendor admin APIs.
FIREMOOT_S3_REGIONus-east-1Region for request signing.
FIREMOOT_S3_ACCESS_KEY(required)Access key id.
FIREMOOT_S3_SECRET_KEY(required)Secret access key.
FIREMOOT_S3_PUBLIC_URL(unset)Base URL for public object reads. Defaults to <endpoint>/<bucket>.
FIREMOOT_S3_FORCE_PATH_STYLEtruePath-style (endpoint/bucket/key) vs virtual-host (bucket.endpoint/key) addressing.
FIREMOOT_S3_PRESIGN_EXPIRY_SECONDS900Lifetime of a presigned upload URL.
FIREMOOT_MEDIA_MAX_IMAGE_BYTES10485760 (10 MiB)Per-image upload cap.
FIREMOOT_MEDIA_MAX_FILE_BYTES52428800 (50 MiB)Per-file upload cap.
FIREMOOT_MEDIA_ALLOWED_MIMEsee belowComma-separated MIME allowlist.

The default allowlist is image/png,image/jpeg,image/gif,image/webp,application/pdf,text/plain.

FIREMOOT_S3_FORCE_PATH_STYLE decides how object URLs are built, and it needs to match how your store serves public reads. Path style suits MinIO and friends and is the default. Stores that only serve public objects virtual-host style - Tigris being the one you are most likely to meet, where a path-style public read returns 403 - need it set to false so presigned PUTs and public GETs share the same bucket-subdomain origin. Pair that with an explicit FIREMOOT_S3_PUBLIC_URL on the same origin.

Unread reminders ​

Reminders are off until an interval is set. See webhooks for what they send and when.

VariableDefaultWhat it does
FIREMOOT_UNREAD_REMINDER_INTERVAL_SECONDS(unset)How long a message stays unread before user.unread_message_reminder fires, from 60 to 86400. Setting it enables reminders.
FIREMOOT_UNREAD_REMINDER_MAX_MEMBERS10Channels with more members than this never send reminders.

Development ​

VariableDefaultWhat it does
FIREMOOT_DEV_DEMOfalseServes a demo page at /demo and drops the secure flag from the admin session cookie. Never enable it in production.

JVM flags ​

The published image already runs with -XX:+UseCompactObjectHeaders -XX:+UseG1GC -XX:MaxRAMPercentage=75, so the heap tracks whatever memory limit you give the container. You do not need to set -Xmx. See sizing for what those flags buy.

Operational endpoints ​

PathAuthReturns
GET /healthznone200 {"status":"ok"}. Liveness; never touches the database.
GET /readyznone200 {"status":"ready"}, or 503 when Postgres is unreachable.
GET /metricsnonePrometheus text exposition.
GET /v1/openapi.jsonnoneThe generated OpenAPI document.

/metrics is deliberately unauthenticated so a scraper needs no credential, and it is deliberately cheap - the gauges (firemoot_ccu, firemoot_dau, firemoot_wau, firemoot_mau, firemoot_messages{channel_type=…}, firemoot_media_bytes, firemoot_db_size_bytes) are computed on demand. It does leak usage volumes, so firewall it or restrict it at your reverse proxy if the box is public.

Released under the Apache-2.0 License.