Recording & Transcription (Advanced)¶
Setting up recording and AI transcription requires additional services beyond the base Meet deployment.
Architecture overview¶
Meet Backend ──► LiveKit Egress ──► Garage/S3
▲ │
│ (webhook) │
└──────────────────────────────────┘
│
Summary Service
│ │
Transcribe Summarize
(Whisper) (LLM API)
Part 1: Recording with LiveKit Egress¶
What is Egress?¶
LiveKit Egress is a LiveKit component that records rooms to files. It:
- Captures the full room composite (all video/audio in the current layout)
- Saves the output to S3-compatible storage
- Notifies the Meet backend via an S3 webhook when the file is ready
Step 1: Configure Garage¶
Garage is an open-source, S3-compatible distributed object store designed for self-hosted deployments. It runs on minimal hardware (1GB RAM) and stores data across multiple zones for resilience.
Add Garage to your compose.yml:
garage:
image: docker.io/deuxfleurs/garage:1.9.0
command: server
volumes:
- ./data/garage:/data
ports:
- "3900:3900"
- "3901:3901"
environment:
- RUST_LOG=warn
healthcheck:
test: ["CMD", "garage", "status"]
interval: 10s
timeout: 5s
retries: 5
Create a garage.toml configuration file:
# garage.toml
meta_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
[s3api]
api_key = "meet-access-key"
api_secret = "your-very-long-random-secret-here"
# Expose the S3 API on port 3900
[s3api.local]
bind_addr = "0.0.0.0:3900"
# Admin console on port 3901
[admin]
bind_addr = "0.0.0.0:3901"
Mount the config into the container:
garage:
image: docker.io/deuxfleurs/garage:1.9.0
command: server
volumes:
- ./data/garage:/data
- ./garage.toml:/etc/garage/garage.toml:ro
ports:
- "3900:3900"
- "3901:3901"
Initialize Garage and create the bucket:
# Initialize a single-node cluster
docker compose exec garage garage node id
docker compose exec garage garage node connect --no-s3-proxy
# Create the storage bucket
docker compose exec garage garage bucket create meet-media-storage
# Create an access key for the bucket
docker compose exec garage garage key create meet-access-key
# Grant the access key permission to the bucket
docker compose exec garage garage bucket allow meet-media-storage --key meet-access-key --read --write
Step 2: Configure the recording notification¶
The Meet backend needs to be notified when a recording file is uploaded. Configure Garage to send notifications when objects are created in the meet-media-storage bucket:
# Create an SQS queue for recording notifications
docker compose exec garage garage queue create meet-webhook
# Allow the bucket to send notifications to the queue
docker compose exec garage garage bucket notification allow meet-media-storage --queue meet-webhook --events put --filter "recordings/*"
Set the notification endpoint and credentials in Meet's environment:
The webhook endpoint on the Meet backend (POST /api/v1.0/recordings/storage-hook/) must be reachable from inside Docker. If running behind nginx-proxy, ensure the backend container is on the same Docker network.
Step 3: Set up LiveKit Egress¶
Egress requires its own configuration file. Create livekit-egress.yaml:
api_key: myapikey
api_secret: your-livekit-api-secret
ws_url: ws://livekit:7880
s3:
access_key: meet-access-key
secret: your-very-long-random-secret-here
endpoint: http://garage:3900
bucket: meet-media-storage
region: us-east-1 # required for S3-compatible storage
force_path_style: true # required for Garage
Add to your compose.yml:
livekit-egress:
image: livekit/egress:v1.11.0
environment:
EGRESS_CONFIG_FILE: /livekit-egress.yaml
volumes:
- ./livekit-egress.yaml:/livekit-egress.yaml
depends_on:
redis:
condition: service_healthy
garage:
condition: service_healthy
Step 4: Configure Meet backend for recording¶
AWS_S3_ENDPOINT_URL=http://garage:3900
AWS_S3_ACCESS_KEY_ID=meet-access-key
AWS_S3_SECRET_ACCESS_KEY=your-very-long-random-secret-here
AWS_STORAGE_BUCKET_NAME=meet-media-storage
AWS_S3_SECURE_ACCESS=False
LIVEKIT_API_KEY=myapikey
LIVEKIT_API_SECRET=your-livekit-api-secret
LIVEKIT_API_URL=http://livekit:7880
Configurable recording encoding¶
As of v1.15.0 (unreleased), recording encoding is configurable:
Part 2: AI Transcription & Summarization¶
Transcription requires the Summary service — a separate FastAPI application with Celery workers.
Summary service architecture¶
Summary Service (FastAPI)
├── transcribe-queue worker → Whisper STT
└── summarize-queue worker → LLM API
Step 1: Configure the summary service¶
The summary service is in src/summary/ in the repository. Create its environment file env.d/development/summary:
# Redis
REDIS_URL=redis://redis-summary:6379/0
# Garage/S3 (to download recordings)
AWS_S3_ENDPOINT_URL=http://garage:3900
AWS_S3_ACCESS_KEY_ID=meet-access-key
AWS_S3_SECRET_ACCESS_KEY=your-very-long-random-secret-here
AWS_STORAGE_BUCKET_NAME=meet-media-storage
AWS_S3_SECURE_ACCESS=False
# Meet backend (to report results)
MEET_API_URL=http://app:8000
MEET_API_TOKEN=internal-api-token
# STT configuration
STT_BACKEND=whisper # or 'kyutai' for Kyutai's Moshi
WHISPER_MODEL=large-v3
# LLM for summarization (optional)
LLM_API_URL=https://api.openai.com/v1
LLM_API_KEY=sk-...
LLM_MODEL=gpt-4o
Step 2: Add to compose.yml¶
redis-summary:
image: redis
app-summary:
build:
context: src/summary
env_file:
- env.d/development/summary
ports:
- "8001:8000"
depends_on:
- redis-summary
celery-summary-transcribe:
build:
context: src/summary
command: celery -A summary.core.celery_worker worker --pool=solo -Q transcribe-queue
env_file:
- env.d/development/summary
depends_on:
- redis-summary
- garage
celery-summary-summarize:
build:
context: src/summary
command: celery -A summary.core.celery_worker worker --pool=solo -Q summarize-queue
env_file:
- env.d/development/summary
depends_on:
- redis-summary
Step 3: Connect Meet backend to Summary service¶
Step 4: Configure STT backend¶
The summary service supports multiple STT backends:
| Backend | Notes |
|---|---|
whisper |
OpenAI Whisper (local). Requires significant GPU/CPU. |
kyutai |
Kyutai's Moshi (real-time). |
| External API | Any OpenAI-compatible STT API endpoint. |
For production, use a GPU-enabled worker or an external STT API for reasonable transcription speed.
Multiple transcription workers¶
As of v1.15.0+ (unreleased), multiple transcription workers are supported for parallel processing:
# Helm values
summary:
transcribeWorkers: 3
transcribeEndpoints:
- http://whisper-1:8000
- http://whisper-2:8000
- http://whisper-3:8000
Supported file formats¶
The summary service accepts: .mp4, .webm, .wav, .mp3, .ogg, and additional formats added in v1.14.0+.
Testing the full recording flow¶
- Start a meeting and click Record
- Speak for 30 seconds, then click Stop recording
- Check Garage for the recording file:
docker compose exec garage garage object list meet-media-storage - Check Meet backend logs for the webhook:
docker compose logs app | grep storage-hook - The room owner should receive an email with a download link
- If transcription is configured, check
docker compose logs celery-summary-transcribe
Alternative S3 providers¶
Garage is recommended for self-hosted single-server deployments. Other S3-compatible providers work with Meet as well:
- AWS S3 — production-scale, managed
- OVH Object Storage — European hosting
- Scaleway Object Storage — French hosting
- Ceph RGW — enterprise-grade, self-hosted
All use the same environment variables (AWS_S3_ENDPOINT_URL, AWS_S3_ACCESS_KEY_ID, etc.) and are configured interchangeably.