Skip to content

Commit 96ad6e3

Browse files
committed
feat(sfu): easy deploy
1 parent 7c8e2ff commit 96ad6e3

9 files changed

Lines changed: 916 additions & 3 deletions

File tree

.github/workflows/build-sfu.yml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ on:
66
- develop
77
paths:
88
- "sfu-server/**"
9+
- "types/**"
10+
workflow_dispatch:
11+
inputs:
12+
tag:
13+
description: "Image tag (default: latest)"
14+
required: false
15+
default: "latest"
16+
17+
env:
18+
REGISTRY: ghcr.io
19+
IMAGE_NAME: frappe/meet/sfu-server
920

1021
jobs:
1122
build-and-push:
@@ -18,17 +29,34 @@ jobs:
1829
- name: Checkout code
1930
uses: actions/checkout@v4
2031

32+
- name: Set up Docker Buildx
33+
uses: docker/setup-buildx-action@v3
34+
2135
- name: Log in to GHCR
2236
uses: docker/login-action@v3
2337
with:
24-
registry: ghcr.io
38+
registry: ${{ env.REGISTRY }}
2539
username: ${{ github.actor }}
2640
password: ${{ secrets.GITHUB_TOKEN }}
2741

42+
- name: Extract metadata
43+
id: meta
44+
uses: docker/metadata-action@v5
45+
with:
46+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
47+
tags: |
48+
type=raw,value=latest,enable={{is_default_branch}}
49+
type=raw,value=${{ github.event.inputs.tag || 'latest' }},enable=${{ github.event_name == 'workflow_dispatch' }}
50+
type=sha,prefix=,format=short
51+
type=ref,event=branch
52+
2853
- name: Build and push Docker image
29-
uses: docker/build-push-action@v5
54+
uses: docker/build-push-action@v6
3055
with:
3156
context: .
3257
file: ./sfu-server/Dockerfile
3358
push: true
34-
tags: ghcr.io/frappe/meet/sfu-server:latest
59+
tags: ${{ steps.meta.outputs.tags }}
60+
labels: ${{ steps.meta.outputs.labels }}
61+
cache-from: type=gha
62+
cache-to: type=gha,mode=max

sfu-server/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,84 @@
11
# Frappe Meet SFU Server
2+
3+
Mediasoup-based Selective Forwarding Unit (SFU) for Frappe Meet.
4+
5+
6+
## Production Deployment
7+
8+
### Prerequisites
9+
10+
- A server with Docker and Docker Compose v2 installed
11+
- A domain pointing to the server (e.g., `sfu.example.com`)
12+
- Ports open: `80/tcp`, `443/tcp`, `40000-40200/udp`
13+
14+
### Quick Start
15+
16+
```bash
17+
# Install on the server (downloads deploy files to /opt/meet-sfu)
18+
curl -fsSL https://raw.githubusercontent.com/frappe/meet/develop/sfu-server/deploy/install.sh | bash
19+
20+
# Configure
21+
cd /opt/meet-sfu
22+
nano .env
23+
```
24+
25+
Set the required values in `.env`:
26+
27+
| Variable | Description | Example |
28+
|---|---|---|
29+
| `JWT_SECRET` | Shared secret with Frappe (generate: `openssl rand -base64 32`) | `a1B2c3D4...` |
30+
| `WEBRTC_ANNOUNCED_IP` | Server's public IP (find: `curl -4 ifconfig.me`) | `203.0.113.10` |
31+
| `DOMAIN` | Domain pointing to this server | `sfu.example.com` |
32+
| `SSL_EMAIL` | Email for Let's Encrypt notifications | `admin@example.com` |
33+
34+
Then run setup:
35+
36+
```bash
37+
./deploy.sh setup
38+
```
39+
40+
This will pull the SFU image, provision an SSL certificate, and start everything.
41+
42+
### Frappe Configuration
43+
44+
Add to your Frappe site's `site_config.json`:
45+
46+
```json
47+
{
48+
"sfu_server_url": "https://sfu.example.com",
49+
"sfu_secret": "<same JWT_SECRET from .env>"
50+
}
51+
```
52+
53+
### Management Commands
54+
55+
```bash
56+
./deploy.sh start # Start all services
57+
./deploy.sh stop # Stop all services
58+
./deploy.sh restart # Restart all services
59+
./deploy.sh update # Pull latest image and restart SFU
60+
./deploy.sh logs # Tail logs (use: ./deploy.sh logs sfu)
61+
./deploy.sh status # Show health and container status
62+
./deploy.sh ssl-renew # Force SSL certificate renewal
63+
```
64+
65+
### Deploying Behind Cloudflare / Reverse Proxy
66+
67+
If SSL is handled upstream, set `DISABLE_SSL=true` in `.env`. This runs nginx on port 80 only and skips certificate provisioning.
68+
69+
### Updating
70+
71+
When new changes are pushed to `develop`, the GitHub Actions workflow builds and pushes a new Docker image. To update the SFU on your server:
72+
73+
```bash
74+
cd /opt/meet-sfu
75+
./deploy.sh update
76+
```
77+
78+
### Firewall Rules
79+
80+
| Port | Protocol | Purpose |
81+
|---|---|---|
82+
| 80 | TCP | HTTP / ACME challenges |
83+
| 443 | TCP | HTTPS |
84+
| 40000-40200 | UDP | WebRTC media |

sfu-server/deploy/.env.example

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# ==============================================================================
2+
# Frappe Meet SFU — Deployment Configuration
3+
# ==============================================================================
4+
# Copy this file to .env and fill in the values before running deploy.sh
5+
#
6+
# cp .env.example .env
7+
# nano .env
8+
# ./deploy.sh setup
9+
# ==============================================================================
10+
11+
# === Domain & SSL =============================================================
12+
# The public domain that points to this server (A/AAAA record).
13+
# Example: https://sfu.meet.example.com
14+
DOMAIN=https://sfu.example.com
15+
16+
# Email for Let's Encrypt certificate notifications.
17+
SSL_EMAIL=admin@example.com
18+
19+
# Set to "true" to skip SSL and run nginx on port 80 only.
20+
# Useful when deploying behind Cloudflare or another reverse proxy that
21+
# already terminates SSL for you.
22+
DISABLE_SSL=false
23+
24+
# === SFU Image ================================================================
25+
# Docker image to pull. The GitHub Actions workflow pushes to this tag.
26+
SFU_IMAGE=ghcr.io/frappe/meet/sfu-server:latest
27+
28+
# === SFU Server ===============================================================
29+
# JWT secret — MUST match the "sfu_secret" in your Frappe site_config.json.
30+
# Generate one with: openssl rand -base64 32
31+
JWT_SECRET=change-me-to-a-strong-random-string
32+
33+
# Internal port (no need to change unless you know why).
34+
PORT=3000
35+
HOST=0.0.0.0
36+
37+
# === WebRTC / Mediasoup =======================================================
38+
# Your server's PUBLIC IP address. Clients will use this IP for WebRTC media.
39+
WEBRTC_ANNOUNCED_IP=
40+
41+
# UDP port range for WebRTC media. ~2 ports per participant per media track.
42+
# 200 ports ≈ 50 concurrent users. Increase if you need more.
43+
RTC_MIN_PORT=40000
44+
RTC_MAX_PORT=40200
45+
46+
# Number of mediasoup workers. Leave blank to auto-detect from CPU cores.
47+
MEDIASOUP_NUM_WORKERS=
48+
49+
# Worker log verbosity: debug | warn | error | none
50+
MEDIASOUP_WORKER_LOGLEVEL=warn
51+
52+
# === Logging ==================================================================
53+
SFU_LOG_LEVEL=info

sfu-server/deploy/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Active environment file (contains secrets)
2+
.env
3+
4+
# Let's Encrypt data (managed by Docker volumes, not committed)
5+
certbot-data/
6+
7+
# Temp files generated during SSL provisioning
8+
nginx/templates/_acme_temp.conf.template

0 commit comments

Comments
 (0)