Files
crm-calls/docker-compose.yml

235 lines
9.0 KiB
YAML

# Jitsi Meet for Tvorymo CRM, based on github.com/jitsi/docker-jitsi-meet (stable-11248).
# Deployed by Coolify (build_pack: dockercompose) on the Volcanic VPS (164.90.233.156).
#
# Routing:
# https://meet.volcanic.tes.gd (443, Coolify Traefik) -> web:8000 (nginx)
# / -> Jitsi web app
# /xmpp-websocket -> prosody:5280 (proxied by web's nginx)
# UDP JVB_PORT (10000) -> jvb (media + SCTP bridge channel), published on the host, bypasses Traefik.
#
# Auth: JWT only (AUTH_TYPE=jwt, ENABLE_GUESTS=0). Passes are issued by the CRM calls module
# (docker-compose.calls.yml, https://crm-calls.volcanic.tes.gd): HS256, iss tvorymo-crm, aud jitsi,
# one room, context.user.moderator -> owner via prosody token_affiliation.
#
# Voice-only calls: jitsi/custom-config.js is appended to the server config.js (browser and mobile app).
# Recording: jibri (one instance, started by the calls module over its HTTP API on the crm-calls-bridge network),
# jitsi/finalize.sh keeps only audio and deletes the video; files go to RECORDINGS_HOST_DIR shared with the module.
#
# Secrets (JICOFO_AUTH_PASSWORD, JVB_AUTH_PASSWORD, JWT_APP_SECRET, JIBRI_RECORDER_PASSWORD, JIBRI_XMPP_PASSWORD)
# live only in Coolify env vars.
services:
web:
# ghcr.io/jitsi/web + jitsi/custom-config.js (voice-only), see jitsi/web.Dockerfile.
build:
context: ./jitsi
dockerfile: web.Dockerfile
restart: unless-stopped
read_only: true
tmpfs:
- /run:size=16M,mode=1750,exec
- /tmp:size=16M,mode=1777,noexec
expose:
- '8000'
volumes:
- jitsi-web-config:/config
- jitsi-web-storage:/storage
environment:
TZ: '${TZ:-Europe/Kyiv}'
PUBLIC_URL: '${PUBLIC_URL:-https://meet.volcanic.tes.gd}'
DISABLE_HTTPS: '1'
ENABLE_HTTP_REDIRECT: '0'
ENABLE_LETSENCRYPT: '0'
ENABLE_XMPP_WEBSOCKET: '1'
XMPP_SERVER: prosody
XMPP_BOSH_URL_BASE: 'http://prosody:5280'
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_GUEST_DOMAIN: guest.meet.jitsi
XMPP_MUC_DOMAIN: muc.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
DEFAULT_LANGUAGE: '${DEFAULT_LANGUAGE:-uk}'
ENABLE_AUTH: '1'
AUTH_TYPE: jwt
ENABLE_GUESTS: '0'
# The calls module shows its own "open in app / continue in browser" page.
DISABLE_DEEP_LINKING: 'true'
# Recording on: config gets hiddenDomain, so jibri can join as the hidden recorder.
ENABLE_RECORDING: '1'
START_AUDIO_ONLY: 'true'
START_WITH_VIDEO_MUTED: 'true'
# Only the calls module may embed Jitsi (call window). Jitsi's nginx sets no X-Frame-Options.
CSP_HEADER: "frame-ancestors ${CALLS_ORIGIN:-https://crm-calls.volcanic.tes.gd}"
depends_on:
- jvb
prosody:
image: ghcr.io/jitsi/prosody:${JITSI_IMAGE_VERSION:-stable-11248}
restart: unless-stopped
read_only: true
tmpfs:
- /run:size=16M,mode=1750,exec
- /tmp:size=16M,mode=1777,noexec
expose:
- '5222'
- '5347'
- '5280'
volumes:
- jitsi-prosody-config:/config
- jitsi-prosody-plugins:/prosody-plugins-custom
- jitsi-prosody-data:/var/lib/prosody
environment:
TZ: '${TZ:-Europe/Kyiv}'
PUBLIC_URL: '${PUBLIC_URL:-https://meet.volcanic.tes.gd}'
XMPP_SERVER: prosody
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_GUEST_DOMAIN: guest.meet.jitsi
XMPP_MUC_DOMAIN: muc.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
ENABLE_XMPP_WEBSOCKET: '1'
JICOFO_AUTH_PASSWORD: '${JICOFO_AUTH_PASSWORD}'
JVB_AUTH_PASSWORD: '${JVB_AUTH_PASSWORD}'
ENABLE_AUTH: '1'
AUTH_TYPE: jwt
ENABLE_GUESTS: '0'
JWT_ALLOW_EMPTY: '0'
JWT_APP_ID: '${JWT_APP_ID:-tvorymo-crm}'
JWT_APP_SECRET: '${JWT_APP_SECRET}'
JWT_ACCEPTED_ISSUERS: '${JWT_APP_ID:-tvorymo-crm}'
JWT_ACCEPTED_AUDIENCES: '${JWT_AUDIENCE:-jitsi}'
# context.user.moderator=true in the pass -> room owner (moderator); guests -> member.
XMPP_MUC_MODULES: token_affiliation
# HTTP /room?room=… (occupants) on prosody:5280 — the calls module polls it (concurrency limit, recording start/stop).
XMPP_MODULES: muc_size
ENABLE_RECORDING: '1'
JIBRI_RECORDER_PASSWORD: '${JIBRI_RECORDER_PASSWORD}'
JIBRI_XMPP_PASSWORD: '${JIBRI_XMPP_PASSWORD}'
networks:
default:
calls-bridge:
aliases:
- jitsi-prosody
jicofo:
image: ghcr.io/jitsi/jicofo:${JITSI_IMAGE_VERSION:-stable-11248}
restart: unless-stopped
read_only: true
tmpfs:
- /run:size=32M,mode=1750,exec
- /tmp:size=16M,mode=1777,noexec
volumes:
- jitsi-jicofo-config:/config
environment:
TZ: '${TZ:-Europe/Kyiv}'
XMPP_SERVER: prosody
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_MUC_DOMAIN: muc.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
JICOFO_AUTH_PASSWORD: '${JICOFO_AUTH_PASSWORD}'
# Prosody checks the pass. Jicofo's own auth must be off: with it, jicofo makes every
# authenticated (= every JWT) participant a moderator, guests included.
# Moderators come only from the pass (prosody token_affiliation); nobody becomes owner automatically.
JICOFO_ENABLE_AUTH: '0'
ENABLE_AUTO_OWNER: 'false'
ENABLE_RECORDING: '1'
JIBRI_XMPP_PASSWORD: '${JIBRI_XMPP_PASSWORD}'
# Java heap (default 3072m — more than the whole server).
JICOFO_MAX_MEMORY: '${JICOFO_MAX_MEMORY:-256m}'
depends_on:
- prosody
jvb:
image: ghcr.io/jitsi/jvb:${JITSI_IMAGE_VERSION:-stable-11248}
restart: unless-stopped
read_only: true
tmpfs:
- /run:size=32M,mode=1750,exec
- /tmp:size=16M,mode=1777,noexec
ports:
# Media must reach JVB directly (not via Traefik): UDP JVB_PORT must be open
# in the VPS firewall. Port and advertised IP come from Coolify env.
- '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp'
volumes:
- jitsi-jvb-config:/config
environment:
TZ: '${TZ:-Europe/Kyiv}'
PUBLIC_URL: '${PUBLIC_URL:-https://meet.volcanic.tes.gd}'
XMPP_SERVER: prosody
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
JVB_AUTH_PASSWORD: '${JVB_AUTH_PASSWORD}'
JVB_PORT: '${JVB_PORT:-10000}'
# Comma-separated public IP(s) announced to clients; set in Coolify env (164.90.233.156).
JVB_ADVERTISE_IPS: '${JVB_ADVERTISE_IPS:-164.90.233.156}'
# Java heap (default 3072m — more than the whole server).
VIDEOBRIDGE_MAX_MEMORY: '${VIDEOBRIDGE_MAX_MEMORY:-512m}'
depends_on:
- prosody
# One recorder (MAX_CONCURRENT_CALLS=1). Started/stopped by the calls module via http://jitsi-jibri:2222.
# Minimal video (800x480, 5 fps): only the audio is kept, see jitsi/finalize.sh.
jibri:
# ghcr.io/jitsi/jibri + jitsi/finalize.sh, see jitsi/jibri.Dockerfile.
build:
context: ./jitsi
dockerfile: jibri.Dockerfile
restart: unless-stopped
read_only: true
tmpfs:
- /run:size=256M,mode=1750,exec
- /tmp:size=256M,mode=1777,noexec
shm_size: '1gb'
volumes:
- jitsi-jibri-config:/config
- jitsi-jibri-storage:/storage
- '${RECORDINGS_HOST_DIR:-/data/crm-calls/recordings}:/recordings'
environment:
TZ: '${TZ:-Europe/Kyiv}'
PUBLIC_URL: '${PUBLIC_URL:-https://meet.volcanic.tes.gd}'
XMPP_SERVER: prosody
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_MUC_DOMAIN: muc.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
XMPP_HIDDEN_DOMAIN: hidden.meet.jitsi
JIBRI_RECORDER_PASSWORD: '${JIBRI_RECORDER_PASSWORD}'
JIBRI_XMPP_PASSWORD: '${JIBRI_XMPP_PASSWORD}'
JIBRI_HTTP_API_EXTERNAL_PORT: '2222'
JIBRI_RECORDING_DIR: /recordings
JIBRI_FINALIZE_RECORDING_SCRIPT_PATH: /opt/crm-calls/finalize.sh
JIBRI_RECORDING_RESOLUTION: '800x480'
JIBRI_RECORDING_FRAMERATE: '5'
JIBRI_RECORDING_CONSTANT_RATE_FACTOR: '40'
JIBRI_RECORDING_VIDEO_ENCODE_PRESET_RECORDING: ultrafast
# Jibri's own JVM: small heap. Voice calls: people may stay muted/silent for long —
# Jibri must not stop the recording on "no media"/"all muted"; the module stops it when the room empties.
JAVA_TOOL_OPTIONS: '-Xmx256m -Djibri.call-status-checks.no-media-timeout=4h -Djibri.call-status-checks.all-muted-timeout=4h -Djibri.call-status-checks.default-call-empty-timeout=60s'
CALLS_AUDIO_BITRATE: '${CALLS_AUDIO_BITRATE:-32k}'
CALLS_CHUNK_SECONDS: '${CALLS_CHUNK_SECONDS:-1200}'
depends_on:
- jicofo
networks:
default:
calls-bridge:
aliases:
- jitsi-jibri
networks:
# Shared with the calls module app (docker-compose.calls.yml, external network of the same name).
calls-bridge:
name: crm-calls-bridge
volumes:
jitsi-jibri-config:
jitsi-jibri-storage:
jitsi-web-config:
jitsi-web-storage:
jitsi-prosody-config:
jitsi-prosody-plugins:
jitsi-prosody-data:
jitsi-jicofo-config:
jitsi-jvb-config: