Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 146 additions & 132 deletions .env

Large diffs are not rendered by default.

41 changes: 27 additions & 14 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,15 @@ MAX_PER_GROUP=4
MAX_USERNAME_LENGTH=10
DISABLE_ANONYMOUS=false

# Configure low and recommended bandwidth used by video and screen share in the peer-to-peer connection (in kbit/s)
PEER_VIDEO_LOW_BANDWIDTH=150
PEER_VIDEO_RECOMMENDED_BANDWIDTH=600
PEER_SCREEN_SHARE_LOW_BANDWIDTH=250
PEER_SCREEN_SHARE_RECOMMENDED_BANDWIDTH=1000

# The version of the docker image to use
# MUST uncomment "image" keys in the docker-compose file for it to be effective
VERSION=v1.28.0
VERSION=v1.28.9

TZ=Europe/Paris

Expand All @@ -55,21 +61,24 @@ LIVEKIT_API_KEY=
LIVEKIT_API_SECRET=

#
# Meeting Recording (LiveKit Egress)
# Meeting Recording (Livekit Egress)
# To enable meeting recording, you must have Livekit Egress installed and configured.
# See: https://docs.livekit.io/egress-ingress/egress/overview/
# For WorkAdventure configuration, see: https://github.com/workadventure/workadventure/blob/master/docs/others/self-hosting/recording.md
#

# Comma-separated list of meeting room names to record (e.g. "meeting_room_1,meeting_room_2")
# Labels are the suffix after localWorld.<id>- in the meeting room name.
RECORDING_MEETING_ROOMS=

# MinIO region (e.g., us-east-1, eu-west-1)
MINIO_REGION=
# MinIO access credentials
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
# MinIO bucket name for recordings
MINIO_BUCKET=

# S3 endpoint for storing recordings (e.g., https://s3.eu-west-1.amazonaws.com or http://rustfs:9000)
LIVEKIT_RECORDING_S3_ENDPOINT=
# Public URL for serving recordings (optional, defaults to LIVEKIT_RECORDING_S3_ENDPOINT)
# Use this if your S3 endpoint is internal/private and you need a separate public URL
LIVEKIT_RECORDING_S3_CDN_ENDPOINT=
# S3 access credentials
LIVEKIT_RECORDING_S3_ACCESS_KEY=
LIVEKIT_RECORDING_S3_SECRET_KEY=
# S3 bucket name for recordings
LIVEKIT_RECORDING_S3_BUCKET=
# S3 region (e.g., us-east-1, eu-west-1)
LIVEKIT_RECORDING_S3_REGION=

#
# Jitsi
Expand Down Expand Up @@ -342,3 +351,7 @@ GOOGLE_DRIVE_PICKER_CLIENT_ID=
GOOGLE_DRIVE_PICKER_APP_ID=

MAX_USERS_FOR_WEBRTC=4

# Conversation bubble
MINIMUM_DISTANCE=64
GROUP_RADIUS=48
14 changes: 1 addition & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ up-f:
$(OPTIONAL_P)
@${DR} ${DC} up -d --force-recreate $(P)

up-b:
$(OPTIONAL_P)
@${DR} ${DC} up -d --build $(P)

stop:
$(OPTIONAL_P)
@${DR} ${DC} stop $(P)
Expand Down Expand Up @@ -160,12 +156,4 @@ wa-dev:
wa-upload:
@cd maps && ${DR} npm run upload

# LiveKit

lk-room-list:
@${DR} ${DC} exec webhook bash -c 'lk room list'

lk-egress-list:
@${DR} ${DC} exec webhook bash -c 'lk egress list'

.PHONY: ssm ssh rsync exec up up-f up-b stop restart logs ps encrypt decrypt tf-init tf-plan tf-apply tf-destroy wa-init wa-update wa-dev wa-upload lk-room-list lk-egress-list
.PHONY: ssm ssh rsync exec up up-f stop restart logs ps encrypt decrypt tf-init tf-plan tf-apply tf-destroy wa-init wa-update wa-dev wa-upload
35 changes: 16 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@

- This guide supports both AWS EC2 and Azure VM with Terraform.

### Notes

- Ensure that meeting room names and user display names are unique.

### Prepare Repository

```bash
Expand Down Expand Up @@ -422,7 +418,7 @@ POSTGRES_PASSWORD=<UNIQUE_RANDOM_32_HEX>
7. Click Sign in
8. After successful authentication, you will be redirected back to Element and logged in

## Set up Egress with MinIO
## Set up Egress with RustFS

```bash
# VM
Expand All @@ -444,26 +440,27 @@ make up-f

```env
# Required
RECORDING_MEETING_ROOMS=<RECORDING_MEETING_ROOMS>
MINIO_REGION=ap-northeast-1
MINIO_ACCESS_KEY=<UNIQUE_RANDOM_64_HEX>
MINIO_SECRET_KEY=<UNIQUE_RANDOM_64_HEX>
MINIO_BUCKET=livekit-recording
LIVEKIT_RECORDING_S3_ENDPOINT=http://rustfs-livekit:9000
LIVEKIT_RECORDING_S3_CDN_ENDPOINT=https://cdn-livekit.<YOUR_FQDN>
LIVEKIT_RECORDING_S3_ACCESS_KEY=<UNIQUE_RANDOM_64_HEX>
LIVEKIT_RECORDING_S3_SECRET_KEY=<UNIQUE_RANDOM_64_HEX>
LIVEKIT_RECORDING_S3_BUCKET=livekit-recordings
LIVEKIT_RECORDING_S3_REGION=ap-northeast-1
MAX_USERS_FOR_WEBRTC=0
```

1. Add A records in your DNS provider to point your domain to the VM public IP

| Record Name | Type | Value | TTL |
| ------------------------- | ---- | ------------------------ | --- |
| cdn-livekit.<YOUR_FQDN> | A | <VM_PUBLIC_IPV4_ADDRESS> | 300 |
| minio-livekit.<YOUR_FQDN> | A | <VM_PUBLIC_IPV4_ADDRESS> | 300 |
| Record Name | Type | Value | TTL |
| -------------------------- | ---- | ------------------------ | --- |
| cdn-livekit.<YOUR_FQDN> | A | <VM_PUBLIC_IPV4_ADDRESS> | 300 |
| rustfs-livekit.<YOUR_FQDN> | A | <VM_PUBLIC_IPV4_ADDRESS> | 300 |

## Log in to MinIO
## Log in to RustFS

1. Access MinIO Web: `https://minio-livekit.<YOUR_FQDN>`
2. Enter your MinIO credentials:
- Username: <MINIO_ACCESS_KEY>
- Password: <MINIO_SECRET_KEY>
1. Access RustFS Console: `https://rustfs-livekit.<YOUR_FQDN>`
2. Enter your RustFS credentials:
- Username: <LIVEKIT_RECORDING_S3_ACCESS_KEY>
- Password: <LIVEKIT_RECORDING_S3_SECRET_KEY>
3. Click Sign in
4. After successful authentication, you will be logged in
120 changes: 71 additions & 49 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ services:
- EMBEDDED_DOMAINS_WHITELIST=${EMBEDDED_DOMAINS_WHITELIST}
- CARDS_ENABLED=${CARDS_ENABLED}
- TLDRAW_ENABLED=${TLDRAW_ENABLED}
- MINIMUM_DISTANCE=${MINIMUM_DISTANCE}
# Google drive picker
- GOOGLE_DRIVE_PICKER_CLIENT_ID=${GOOGLE_DRIVE_PICKER_CLIENT_ID}
- GOOGLE_DRIVE_PICKER_APP_ID=${GOOGLE_DRIVE_PICKER_APP_ID}
Expand All @@ -114,6 +115,13 @@ services:
- MATRIX_ADMIN_USER
- MATRIX_ADMIN_PASSWORD
- MATRIX_DOMAIN
- LIVEKIT_RECORDING_S3_ENDPOINT
- LIVEKIT_RECORDING_S3_ACCESS_KEY
- LIVEKIT_RECORDING_S3_SECRET_KEY
- LIVEKIT_RECORDING_S3_REGION
- LIVEKIT_RECORDING_S3_BUCKET
- LIVEKIT_RECORDING_S3_CDN_ENDPOINT
- MAX_DISPLAYED_VIDEOS
labels:
traefik.enable: "true"

Expand Down Expand Up @@ -152,6 +160,8 @@ services:
image: thecodingmachine/workadventure-back:${VERSION}
environment:
- PLAY_URL=https://${DOMAIN}
- MINIMUM_DISTANCE=${MINIMUM_DISTANCE}
- GROUP_RADIUS=${GROUP_RADIUS}
- SECRET_JITSI_KEY
- ENABLE_MAP_EDITOR
- SECRET_KEY
Expand Down Expand Up @@ -182,6 +192,12 @@ services:
- LIVEKIT_HOST
- LIVEKIT_API_KEY
- LIVEKIT_API_SECRET
- LIVEKIT_RECORDING_S3_ENDPOINT
- LIVEKIT_RECORDING_S3_ACCESS_KEY
- LIVEKIT_RECORDING_S3_SECRET_KEY
- LIVEKIT_RECORDING_S3_REGION
- LIVEKIT_RECORDING_S3_BUCKET
- LIVEKIT_RECORDING_S3_CDN_ENDPOINT
labels:
traefik.enable: "true"
traefik.http.middlewares.strip-api-prefix.stripprefix.prefixes: "/api"
Expand All @@ -195,6 +211,9 @@ services:
traefik.http.routers.back-ssl.service: "back"
traefik.http.routers.back-ssl.tls: "true"
traefik.http.routers.back-ssl.tls.certresolver: "myresolver"
depends_on:
rustfs-livekit-init:
condition: service_completed_successfully
restart: ${RESTART_POLICY}

uploader:
Expand Down Expand Up @@ -245,6 +264,15 @@ services:
traefik.http.routers.icon-ssl.service: "icon"
traefik.http.routers.icon-ssl.tls: "true"
traefik.http.routers.icon-ssl.tls.certresolver: "myresolver"
# Route for /lettericons (redirect target from iconserver)
traefik.http.routers.lettericons.rule: "Host(`${DOMAIN}`) && PathPrefix(`/lettericons`)"
traefik.http.routers.lettericons.entryPoints: "web"
traefik.http.routers.lettericons.service: "icon"
traefik.http.routers.lettericons-ssl.rule: "Host(`${DOMAIN}`) && PathPrefix(`/lettericons`)"
traefik.http.routers.lettericons-ssl.entryPoints: "websecure"
traefik.http.routers.lettericons-ssl.service: "icon"
traefik.http.routers.lettericons-ssl.tls: "true"
traefik.http.routers.lettericons-ssl.tls.certresolver: "myresolver"
restart: ${RESTART_POLICY}

redis:
Expand Down Expand Up @@ -319,44 +347,18 @@ services:
condition: service_started
restart: ${RESTART_POLICY}

webhook:
build:
context: .
dockerfile: webhook/Dockerfile
volumes:
- ./egress/bin/start.sh:/app/start.sh:ro
environment:
- LIVEKIT_API_KEY
- LIVEKIT_API_SECRET
- MINIO_REGION
- MINIO_ACCESS_KEY
- MINIO_SECRET_KEY
- MINIO_BUCKET
- RECORDING_MEETING_ROOMS
- LIVEKIT_URL=http://livekit:7880
- MINIO_URL=http://minio-livekit:9000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
interval: 5s
timeout: 3s
retries: 3
depends_on:
livekit:
condition: service_started
egress:
condition: service_started
restart: ${RESTART_POLICY}

egress:
image: livekit/egress:v1.9.0
entrypoint: ["/entrypoint.sh"]
cap_add:
- SYS_ADMIN
privileged: true
shm_size: "2gb"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
LIVEKIT_API_KEY:
LIVEKIT_API_SECRET:
MINIO_REGION:
MINIO_ACCESS_KEY:
MINIO_SECRET_KEY:
MINIO_BUCKET:
DISABLE_CUDA: "true"
DISABLE_NVENC: "true"
volumes:
Expand All @@ -375,24 +377,20 @@ services:
condition: service_started
restart: ${RESTART_POLICY}

minio-livekit:
image: bitnamilegacy/minio:2025.4.22
environment:
MINIO_ROOT_USER: "${MINIO_ACCESS_KEY}"
MINIO_ROOT_PASSWORD: "${MINIO_SECRET_KEY}"
MINIO_DEFAULT_BUCKETS: "${MINIO_BUCKET}:private"
MINIO_SITE_REGION: "${MINIO_REGION}"
rustfs-livekit:
image: rustfs/rustfs:1.0.0-alpha.83
command: ["/data"]
labels:
- "traefik.enable=true"
- "traefik.http.routers.minio-livekit.rule=Host(`minio-livekit.${DOMAIN}`)"
- "traefik.http.routers.minio-livekit.entryPoints=web"
- "traefik.http.services.minio-livekit.loadbalancer.server.port=9001"
- "traefik.http.routers.minio-livekit.service=minio-livekit"
- "traefik.http.routers.minio-livekit-ssl.rule=Host(`minio-livekit.${DOMAIN}`)"
- "traefik.http.routers.minio-livekit-ssl.entryPoints=websecure"
- "traefik.http.routers.minio-livekit-ssl.tls=true"
- "traefik.http.routers.minio-livekit-ssl.tls.certresolver=myresolver"
- "traefik.http.routers.minio-livekit-ssl.service=minio-livekit"
- "traefik.http.routers.rustfs-livekit.rule=Host(`rustfs-livekit.${DOMAIN}`)"
- "traefik.http.routers.rustfs-livekit.entryPoints=web"
- "traefik.http.services.rustfs-livekit.loadbalancer.server.port=9001"
- "traefik.http.routers.rustfs-livekit.service=rustfs-livekit"
- "traefik.http.routers.rustfs-livekit-ssl.rule=Host(`rustfs-livekit.${DOMAIN}`)"
- "traefik.http.routers.rustfs-livekit-ssl.entryPoints=websecure"
- "traefik.http.routers.rustfs-livekit-ssl.tls=true"
- "traefik.http.routers.rustfs-livekit-ssl.tls.certresolver=myresolver"
- "traefik.http.routers.rustfs-livekit-ssl.service=rustfs-livekit"
- "traefik.http.routers.cdn-livekit.rule=Host(`cdn-livekit.${DOMAIN}`)"
- "traefik.http.routers.cdn-livekit.entryPoints=web"
- "traefik.http.services.cdn-livekit.loadbalancer.server.port=9000"
Expand All @@ -402,8 +400,32 @@ services:
- "traefik.http.routers.cdn-livekit-ssl.tls=true"
- "traefik.http.routers.cdn-livekit-ssl.tls.certresolver=myresolver"
- "traefik.http.routers.cdn-livekit-ssl.service=cdn-livekit"
environment:
RUSTFS_ACCESS_KEY: "${LIVEKIT_RECORDING_S3_ACCESS_KEY}"
RUSTFS_SECRET_KEY: "${LIVEKIT_RECORDING_S3_SECRET_KEY}"
RUSTFS_CONSOLE_ENABLE: "true"
restart: ${RESTART_POLICY}

rustfs-livekit-init:
image: amazon/aws-cli:2.31.1
depends_on:
rustfs-livekit:
condition: service_started
entrypoint: ["/bin/sh", "-ec"]
command:
- |
until aws --endpoint-url http://rustfs-livekit:9000 s3api list-buckets >/dev/null 2>&1; do
sleep 1
done
aws --endpoint-url http://rustfs-livekit:9000 s3api head-bucket --bucket ${LIVEKIT_RECORDING_S3_BUCKET} >/dev/null 2>&1 \
|| aws --endpoint-url http://rustfs-livekit:9000 s3api create-bucket \
--bucket ${LIVEKIT_RECORDING_S3_BUCKET} \
--create-bucket-configuration LocationConstraint=${LIVEKIT_RECORDING_S3_REGION}
environment:
AWS_ACCESS_KEY_ID: "${LIVEKIT_RECORDING_S3_ACCESS_KEY}"
AWS_SECRET_ACCESS_KEY: "${LIVEKIT_RECORDING_S3_SECRET_KEY}"
AWS_DEFAULT_REGION: "${LIVEKIT_RECORDING_S3_REGION}"

cert-dumper:
image: ldez/traefik-certs-dumper:v2.8.1
command: file --version v2 --watch --source /letsencrypt/acme.json --dest /letsencrypt/certs
Expand Down Expand Up @@ -438,7 +460,7 @@ services:
restart: ${RESTART_POLICY}

synapse:
image: matrixdotorg/synapse:v1.100.0
image: matrixdotorg/synapse:v1.140.0
entrypoint: ["/entrypoint.sh"]
environment:
DOMAIN: "${DOMAIN}"
Expand Down
Loading