Realm

Configuration

Full reference for config.json and all REALM_ environment variables.

The server reads config from a config.json file and/or REALM_-prefixed environment variables. Environment variables always take priority over config.json.

Config file

Create a config.json in the same directory as your Compose file:

{
  "identity": {
    "name": "My Realm Server",
    "description": "A community server",
    "publicHost": "my-server.example.com",
    "password": null
  },
  "coreApi": {
    "baseUrl": "https://api.realmvoice.app"
  },
  "storage": {
    "mongoUrl": "mongodb://mongo:27017/realm_dedicated"
  },
  "voice": {
    "rtpMin": 40000,
    "rtpMax": 49999
  },
  "webrtc": {
    "listenIp": "0.0.0.0",
    "announcedIp": null
  },
  "attachments": {
    "provider": "local",
    "maxFileSizeMb": 25,
    "localPath": "./uploads"
  },
  "logLevel": "info"
}

Mount it into the container:

# docker-compose.server.yml
volumes:
  - ./config.json:/app/apps/server/config.json:ro

Identity

FieldEnv varDefaultDescription
identity.nameREALM_IDENTITY_NAME"Realm Server"Server name shown in the server browser
identity.descriptionREALM_IDENTITY_DESCRIPTION""Optional description
identity.publicHostREALM_IDENTITY_PUBLIC_HOST(auto-detected)Public hostname/IP clients connect to. Required behind NAT or a reverse proxy
identity.publicPortREALM_IDENTITY_PUBLIC_PORT5000Port clients connect on. Set to 443 when using a reverse proxy
identity.passwordREALM_IDENTITY_PASSWORDnullRequire this password to join. Set to null or omit to allow open access

Realm API

FieldEnv varDefaultDescription
coreApi.baseUrlREALM_CORE_API_BASE_URL"https://api.realmvoice.app"URL of the Realm API

Storage

FieldEnv varDefaultDescription
storage.mongoUrlREALM_STORAGE_MONGO_URL"mongodb://localhost:27017/realm"Database connection string

Voice

FieldEnv varDefaultDescription
voice.rtpMinREALM_VOICE_RTP_MIN40000Start of UDP port range for voice media
voice.rtpMaxREALM_VOICE_RTP_MAX49999End of UDP port range for voice media

WebRTC

FieldEnv varDefaultDescription
webrtc.listenIpREALM_WEBRTC_LISTEN_IP"0.0.0.0"Interface the voice server listens on
webrtc.announcedIpREALM_WEBRTC_ANNOUNCED_IPnullPublic IP for voice ICE candidates. Set to your server's public IP or hostname. Required if behind NAT

webrtc.announcedIp is the most important voice setting. If it's wrong or missing, voice connections will appear to connect but no audio will flow.

HTTPS / TLS

The web client at realmvoice.app is served over HTTPS, and browsers refuse to open an insecure http:// / ws:// connection from an HTTPS page (a "mixed content" error). So a server must be reachable over https:// for the web client to connect.

You have two options:

  • Built-in auto-HTTPS (tls.enabled). Set tls.enabled and a publicHost, then start the bundled Caddy sidecar. Caddy obtains and renews a Let's Encrypt certificate automatically and the server registers itself on port 443. No reverse proxy of your own to configure.
  • Your own reverse proxy. Terminate TLS with Caddy/nginx/Traefik in front of the server yourself and set identity.publicPort to 443.

Enable the built-in option by opting into the tls Compose profile:

# config.json: { "identity": { "publicHost": "my-server.example.com" }, "tls": { "enabled": true } }
# or: REALM_IDENTITY_PUBLIC_HOST=my-server.example.com REALM_TLS_ENABLED=true
docker compose -f docker-compose.server.yml --profile tls up -d

Open ports 80 and 443 on your firewall (80 is needed for the certificate challenge).

FieldEnv varDefaultDescription
tls.enabledREALM_TLS_ENABLEDfalseRun the Caddy sidecar and register on the TLS port
tls.emailREALM_TLS_EMAILnullACME account email for Let's Encrypt expiry notices (optional)
tls.portREALM_TLS_PORT443Public TLS port Caddy listens on and the server registers
tls.upstreamREALM_TLS_UPSTREAMrealm-server:5000Where Caddy reverse-proxies. Change only if you renamed the server service

A real domain in publicHost is the reliable setup. A bare public IP is best-effort: it works only if the Caddy image and certificate authority support IP-address certificates, otherwise the certificate is untrusted. LAN / private IPs (192.168.x.x, 10.x.x.x) can never get a browser-trusted certificate. To use an IP-only server, connect with the desktop app instead, which is not subject to the browser's mixed-content rule.

Attachments

The server supports image and video attachments in chat. Uploads are stored on the local filesystem by default.

Supported formats: PNG, JPEG, GIF, WebP, SVG, MP4, WebM, MOV

Images are automatically compressed and thumbnailed. Videos are stored as-is with no server-side transcoding.

Local storage (default)

{
  "attachments": {
    "provider": "local",
    "maxFileSizeMb": 25,
    "localPath": "./uploads"
  }
}

The Docker Compose setup mounts a volume at /data which includes the uploads/ directory. Files survive container restarts and updates without any extra configuration.

S3-compatible storage

Use S3 for CDN delivery or if you'd rather not store files on disk:

{
  "attachments": {
    "provider": "s3",
    "maxFileSizeMb": 50,
    "s3": {
      "bucket": "my-realm-uploads",
      "region": "us-east-1",
      "endpoint": "https://abc123.r2.cloudflarestorage.com",
      "accessKeyId": "AKIA...",
      "secretAccessKey": "wJal..."
    }
  }
}

Or via environment variables:

REALM_ATTACHMENTS_PROVIDER=s3
REALM_ATTACHMENTS_S3_BUCKET=my-realm-uploads
REALM_ATTACHMENTS_S3_REGION=us-east-1
REALM_ATTACHMENTS_S3_ENDPOINT=https://abc123.r2.cloudflarestorage.com
REALM_ATTACHMENTS_S3_ACCESS_KEY_ID=AKIA...
REALM_ATTACHMENTS_S3_SECRET_ACCESS_KEY=wJal...

Compatible with Cloudflare R2, MinIO, and AWS S3. For R2 and MinIO, set endpoint to the custom endpoint URL.

FieldEnv varDefaultDescription
attachments.providerREALM_ATTACHMENTS_PROVIDER"local""local" or "s3"
attachments.maxFileSizeMbREALM_ATTACHMENTS_MAX_FILE_SIZE_MB25Max upload size per file in MB (1-100)
attachments.localPathREALM_ATTACHMENTS_LOCAL_PATH"./uploads"Directory for local storage
attachments.s3.bucketREALM_ATTACHMENTS_S3_BUCKETnullS3 bucket name
attachments.s3.regionREALM_ATTACHMENTS_S3_REGIONnullAWS region (e.g. "us-east-1")
attachments.s3.endpointREALM_ATTACHMENTS_S3_ENDPOINTnullCustom endpoint for R2/MinIO
attachments.s3.accessKeyIdREALM_ATTACHMENTS_S3_ACCESS_KEY_IDnullAccess key ID
attachments.s3.secretAccessKeyREALM_ATTACHMENTS_S3_SECRET_ACCESS_KEYnullSecret access key

Logging

FieldEnv varDefaultDescription
logLevelREALM_LOG_LEVEL"info"fatal, error, warn, info, debug, trace, or silent

Full environment variable reference

Env varConfig pathExample
REALM_IDENTITY_NAMEidentity.name"My Server"
REALM_IDENTITY_DESCRIPTIONidentity.description"A community server"
REALM_IDENTITY_PUBLIC_HOSTidentity.publicHost"my-server.example.com"
REALM_IDENTITY_PUBLIC_PORTidentity.publicPort443
REALM_IDENTITY_PASSWORDidentity.password"s3cret"
REALM_CORE_API_BASE_URLcoreApi.baseUrl"https://api.realmvoice.app" (default)
REALM_STORAGE_MONGO_URLstorage.mongoUrl"mongodb://mongo:27017/realm"
REALM_VOICE_RTP_MINvoice.rtpMin40000
REALM_VOICE_RTP_MAXvoice.rtpMax49999
REALM_WEBRTC_LISTEN_IPwebrtc.listenIp"0.0.0.0"
REALM_WEBRTC_ANNOUNCED_IPwebrtc.announcedIp"203.0.113.42"
REALM_TLS_ENABLEDtls.enabledtrue
REALM_TLS_EMAILtls.email"[email protected]"
REALM_TLS_PORTtls.port443
REALM_TLS_UPSTREAMtls.upstream"realm-server:5000"
REALM_ATTACHMENTS_PROVIDERattachments.provider"local" or "s3"
REALM_ATTACHMENTS_MAX_FILE_SIZE_MBattachments.maxFileSizeMb25
REALM_ATTACHMENTS_LOCAL_PATHattachments.localPath"./uploads"
REALM_ATTACHMENTS_S3_BUCKETattachments.s3.bucket"my-realm-uploads"
REALM_ATTACHMENTS_S3_REGIONattachments.s3.region"us-east-1"
REALM_ATTACHMENTS_S3_ENDPOINTattachments.s3.endpoint"https://....r2.cloudflarestorage.com"
REALM_ATTACHMENTS_S3_ACCESS_KEY_IDattachments.s3.accessKeyId"AKIA..."
REALM_ATTACHMENTS_S3_SECRET_ACCESS_KEYattachments.s3.secretAccessKey"wJal..."
REALM_LOG_LEVELlogLevel"info"

On this page