---
title: Configuration
description: "Every environment variable kraken v0.9.0 reads, with its default and what it really does, the limits fixed in the code, the settings that live only in sys.config, and the @nolag/core example host's settings."
---

# Configuration

kraken is configured with environment variables, which are substituted into its `sys.config` when the release starts. The defaults below are the ones baked into kraken's Docker image. A few settings exist only in `sys.config`; they are listed at the end of this page.

The repository's `docker-compose.yml` passes only `AUTH_ALLOW_ALL` through from your shell. To set anything else, add it under `environment:` in a compose file, or pass `-e NAME=value` to `docker run`.

This page describes v0.9.0. Where kraken's own `docs/CONFIG.md` says otherwise, this page reflects what the code does.

## Listeners

| Variable | Default | Meaning |
| --- | --- | --- |
| `WS_PORT` | `8080` | HTTP port: the WebSocket endpoint `/ws` and the health check `/health` |
| `MQTT_PORT` | `1883` | Port for kraken's MQTT 3.1.1 listener. **No MQTT client can connect in v0.9.0**: the connection handler fails to start for every connection. Do not expose this port. |

## Backends

| Variable | Default | Values |
| --- | --- | --- |
| `AUTH_BACKEND` | `static` | `static` (a token file), `http` (your service), or an Erlang module name. See [Static Auth File](/docs/self-hosting/static-auth) and [HTTP Auth Contract](/docs/self-hosting/http-auth). |
| `BROKER_BACKEND` | `syn` | `syn` (built in, no dependencies) or `mqtt` (an external MQTT broker), or a module name. `syn` ignores QoS; `mqtt` passes it to the external broker. See [Scaling and MQTT](/docs/self-hosting/scaling). |
| `STORE_BACKEND` | `ets` | `ets` (in memory) or `noop`, or a module name. Where recorded messages go. |
| `CONTROL_BACKEND` | `noop` | `noop` or `http`, or a module name. Usage, subscription and webhook-failure reports. See [Plugins](/docs/self-hosting/plugins#control). |

## Auth and control plane

| Variable | Default | Meaning |
| --- | --- | --- |
| `AUTH_FILE` | `/app/examples/auth.json` | Token file for the `static` backend |
| `AUTH_ALLOW_ALL` | `false` | Accept any token with access to every topic. **Insecure**; local development only. |
| `AUTH_HTTP_URL` | empty | Base URL for the `http` auth backend. Its path is kept. |
| `CONTROL_HTTP_URL` | empty | Base URL for the `http` control backend |
| `BACKEND_SECRET` | empty | Sent as `Authorization: Bearer <secret>` to both HTTP backends |

## MQTT broker backend

Used only with `BROKER_BACKEND=mqtt`.

| Variable | Default | Meaning |
| --- | --- | --- |
| `MQTT_BROKER_HOST` | empty | Host of the external MQTT broker |
| `MQTT_BROKER_PORT` | `1884` | Its port. Most brokers listen on `1883`, so you will usually set this. |
| `MQTT_BROKER_USERNAME`, `MQTT_BROKER_PASSWORD` | empty | Credentials, if the broker wants them |

## Message recording

| Variable | Default | Meaning |
| --- | --- | --- |
| `RECORD_MESSAGES` | `true` | When on, kraken gives every publish a message id and writes it to the store backend. Deliveries then carry `msgId` and `requiresAck: true`, and the SDKs acknowledge them. Nothing in v0.9.0 lets a client read recorded messages back, so for most deployments this only costs memory. The nolag-core quickstart turns it off. |
| `STORE_TTL_SECONDS` | `3600` | `ets` store: how long a recorded message is kept |
| `STORE_MAX_MESSAGES` | `10000` | `ets` store: above this many messages, the oldest tenth is dropped |

## Limits

| Variable | Default | Meaning |
| --- | --- | --- |
| `MAX_MESSAGE_SIZE` | `921600` | **Not enforced in v0.9.0.** Every publish is held to a fixed 921,600 byte ceiling whatever this says, and the same goes for per-token sizes from an auth backend. |

## Internal

| Variable | Default | Meaning |
| --- | --- | --- |
| `INTERNAL_SECRET` | `change_me` | Guards an internal HTTP endpoint on the WebSocket port. It is internal and unsupported: do not build on it. Change the default anyway, and do not route `/internal/` through your reverse proxy. |

## Clustering

See [Scaling and MQTT](/docs/self-hosting/scaling) for how these fit together.

| Variable | Default | Meaning |
| --- | --- | --- |
| `CLUSTER_STRATEGY` | `standalone` | `standalone`, `epmd` or `dns`. `gossip` is accepted but does not form a cluster in v0.9.0. |
| `CLUSTER_HOSTS` | empty | `epmd`: comma-separated full node names of the peers |
| `CLUSTER_DNS_QUERY` | empty | `dns`: a name whose A records are the peers' IP addresses |
| `CLUSTER_NODE_BASENAME` | `kraken_proxy` | `dns`: the part before `@` in each peer's node name. Set it to match your nodes' `ERLANG_NODE_NAME`. |
| `CLUSTER_POLL_INTERVAL` | `30000` | Milliseconds between discovery attempts |
| `ERLANG_NODE_NAME` | `kraken@127.0.0.1` | This node's full name. kraken runs with long names, so the part after `@` must be a fully qualified domain name or an IP address. |
| `ERLANG_COOKIE` | `kraken_dev_cookie` | Shared secret between nodes; must match across the cluster. Change it. |
| `CLUSTER_GOSSIP_PORT`, `CLUSTER_MULTICAST_ADDR` | `45892`, `230.1.1.1` | Used only by `gossip`, which does not work in v0.9.0 |

kraken's `docs/CONFIG.md` also lists `CLUSTER_DNS_NAME` and `CLUSTER_GOSSIP_SECRET`. Nothing in v0.9.0 reads either; DNS discovery uses `CLUSTER_DNS_QUERY`.

## Fixed limits

These are constants in the code, the same for every connection whatever the auth backend returns. We measured each of the first four against v0.9.0.

| Limit | Value | When exceeded |
| --- | --- | --- |
| Publishes per connection | 50 per second | The rest of that second's publishes are refused with error `42910`, `rate_limit_exceeded`. Other frame types do not count. |
| Publish payload | 921,600 bytes, measured as packed MessagePack | Refused with error `42930`, `message_too_large`, carrying `maxSizeBytes` |
| WebSocket frame | 1 MiB (1,048,576 bytes) | The connection is closed with code `1009` |
| Idle connection | 60 seconds without any frame from the client | The connection is closed with code `1000`. The SDKs send a heartbeat every 30 seconds. |
| Auth cache | 30 seconds per token | Successful validations only |
| Revalidation | Every 10 minutes per connection | Checked on the client's heartbeats |
| Filters | 100 per subscription | Refused with `too_many_filters (max 100)` |
| Retained messages (`syn` broker) | The last one per topic, kept 1 hour, in memory | |
| Room-access check | 2 second timeout; refusals remembered for 5 seconds | Configurable in `sys.config` |

## Settings only in sys.config

These have no environment variable. Change them by editing `config/sys.config.src` and rebuilding the image, or in the `sys.config` of an application that [embeds kraken](/docs/self-hosting/plugins#embedding-kraken).

| Key | Default | Meaning |
| --- | --- | --- |
| `cache_miss_fallback_enabled` | `true` | Ask the auth backend's `/check-room-access` when a subscribe misses the cached grants |
| `cache_miss_fallback_timeout_ms` | `2000` | Timeout for that check |
| `acl_deny_cache_ttl_ms` | `5000` | How long a refused check is remembered per actor and address |
| `release_shared_subs_on_close` | `true` | Release a closing connection's load-balanced subscriptions. Applies only to persistent sessions with durable delivery on. |
| `fallback_compat` | `true` | Wildcard-resolved subscriptions also listen on the topic names used by brokers older than protocol version 2, for rolling upgrades |
| `presence_store_backend`, `wake_backend`, `delivery_store_backend` | `noop` | Plugin slots without an environment variable. See [Plugins](/docs/self-hosting/plugins). |
| `durable_delivery` | `false` | Must be `true` for a delivery store backend to take effect |
| `wake_secret` | empty | Signing secret for the `kraken_wake_http` module |

## The @nolag/core example host

The example host in the nolag-core repository reads these. `CoreModule` itself reads no environment variables; a host passes it what it needs.

| Variable | Default | Meaning |
| --- | --- | --- |
| `PORT` | `3000` | HTTP port |
| `POSTGRES_HOST`, `POSTGRES_PORT` | `localhost`, `5432` | Database address |
| `POSTGRES_SOCKET_PATH` | empty | A Unix socket directory; takes precedence over host and port |
| `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DATABASE` | none | Required |
| `POSTGRES_POOL_MIN`, `POSTGRES_POOL_MAX` | `1`, `10` | Connection pool size |
| `SKIP_MIGRATIONS` | `false` | Set to `true` to skip applying core's migrations at startup |
| `SIGNING_KEY_ENCRYPTION_KEY` | empty | 32 random bytes, base64. Needed for signing keys and [client tokens](/docs/client-tokens). |
| `CORS_ORIGINS` | empty | Comma-separated origins allowed to call the host from a browser. `*` is ignored. |
| `DEFAULT_MAX_CONNECTIONS` | unlimited | Connection limit for projects whose document set no limits |
| `DEFAULT_MAX_MESSAGE_SIZE_BYTES` | unlimited | Passed to kraken, which does not enforce it |
| `DEFAULT_SESSION_EXPIRY_SECONDS` | `3600` | Session expiry for `agent` and `orchestrator` actors |
