Files
uPortal/docs/jitsi-nginx-config.md
T
2026-10-15 23:25:46 +03:00

8.1 KiB

Jitsi & Nginx Deployment Configuration

Overview

This document covers the configuration required to deploy uPortal alongside a Docker-based Jitsi Meet instance behind Nginx.


Environment Variables (.env)

These are the variables used by uPortal for Jitsi integration. Copy them into your .env file:

Variable Required Default Purpose
JITSI_SECRET Yes — HMAC-SHA256 shared secret for signing JWTs. Must match the secret configured in Jitsi's prosody (JWT_APP_SECRET). Generate with: openssl rand -hex 32
JITSI_APP_ID Yes — Jitsi App ID. Used as the iss (issuer) and aud (audience) in JWTs. Match this to JWT_APP_ID in Jitsi's prosody config.
JITSI_DOMAIN No meet.jit.si Your Jitsi Meet instance FQDN (e.g. meet.example.com).
SITE_DOMAIN No — The public domain of the portal (e.g. portal.example.com). Required for absolute avatar URLs in JWT context — otherwise Jitsi cannot fetch avatars.
TRUSTED_DOMAIINS No — Comma-separated domains that bypass the external-redirect warning. Add your Jitsi domain here to skip the interstitial page.

Note on TRUSTED_DOMAIINS: The variable name contains a typo (double "I") — this matches the codebase, so keep it as-is.

Minimal .env for Jitsi

JITSI_SECRET=your-64-char-hex-secret-here
JITSI_APP_ID=my_uportal_app
JITSI_DOMAIN=meet.example.com
SITE_DOMAIN=portal.example.com
TRUSTED_DOMAIINS=meet.example.com

Docker-Based Jitsi Meet Setup

1. Prerequisites

  • Docker and Docker Compose installed
  • A server with a public IP and domain names pointing to it
  • Ports 80 and 443 accessible (or behind Nginx)

2. Clone Jitsi Meet Docker

git clone https://github.com/jitsi/docker-jitsi-meet.git
cd docker-jitsi-meet
cp env.example .env

3. Configure Jitsi .env

Edit the Jitsi .env file. The critical settings for JWT authentication:

# --- Domain / TLS ---
CONFIG=~/.jitsi-meet-cfg
HTTP_PORT=8000
HTTPS_PORT=8443

# --- JWT Authentication (REQUIRED for uPortal integration) ---
ENABLE_AUTH=1
AUTH_TYPE=jwt
JWT_APP_ID=my_uportal_app          # Must match uPortal's JITSI_APP_ID
JWT_APP_SECRET=your-64-char-hex-secret-here  # Must match uPortal's JITSI_SECRET
JWT_ACCEPTED_ISSUERS=my_uportal_app
JWT_ACCEPTED_AUDIENCES=my_uportal_app
JWT_ASAP_KEYSERVER=
JWT_ALLOW_EMPTY=false

# --- Optional: Disable guest access ---
# JWT_AUTH_MODULE=jwt

4. Start Jitsi Meet

docker compose up -d

This starts the full Jitsi stack: prosody (XMPP), jicofo (focus), jvb (video bridge), and web (nginx frontend).


Nginx Reverse Proxy Configuration

Sample /etc/nginx/sites-available/uportal

server {
    listen 80;
    server_name portal.example.com meet.example.com;
    return 301 https://$server_name$request_uri;
}

# uPortal
server {
    listen 443 ssl http2;
    server_name portal.example.com;

    ssl_certificate     /etc/letsencrypt/live/portal.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portal.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /static/ {
        proxy_pass http://127.0.0.1:8080;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }
}

# Jitsi Meet
server {
    listen 443 ssl http2;
    server_name meet.example.com;

    ssl_certificate     /etc/letsencrypt/live/meet.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/meet.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;  # Jitsi HTTP port
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support (required for Jitsi)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Large file uploads (recording, streaming)
        client_max_body_size 10M;
        client_body_buffer_size 128k;
    }

    # Long-polling for Jitsi
    location ~ ^/(xmpp|http-bind) {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

Key Points

  • uPortal runs on 127.0.0.1:8080 (use docker compose run -p 127.0.0.1:8080:8080 or systemd service)
  • Jitsi HTTP port is 8000 (from Jitsi's HTTP_PORT=8000), proxied on a different subdomain
  • WebSocket support is essential for Jitsi video/audio — the Upgrade and Connection headers must be forwarded
  • Both services can share the same Nginx instance with different server_name blocks

Docker Compose for uPortal

The project includes docker-compose.yml and Dockerfile for containerized deployment:

version: '3.8'

services:
  uportal:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: uportal
    ports:
      - "127.0.0.1:8080:8080"   # Bind to loopback behind Nginx
    volumes:
      - ./data:/app              # Persist SQLite database locally
    environment:
      LOG_LEVEL: info
      JITSI_SECRET: your-64-char-hex-secret-here
      JITSI_APP_ID: my_uportal_app
      JITSI_DOMAIN: meet.example.com
      SITE_DOMAIN: portal.example.com
      TRUSTED_DOMAIINS: meet.example.com
    restart: unless-stopped

Bind to 127.0.0.1:8080 (not 0.0.0.0:8080) when running behind Nginx on the same host to avoid direct external access.


Architecture Diagram

Internet
    │
    ├── portal.example.com:443  ──┐
    │   Nginx (reverse proxy)     │
    │   ├── / → uPortal :8080     │
    │   └── /static/ → uPortal    │
    │                             │
    └── meet.example.com:443 ────┤
        Nginx (reverse proxy)     │
        ├── / → Jitsi Web :8000   │
        ├── /xmpp → Jitsi         │
        └── /http-bind → Jitsi    │
                                  │
    Docker Network                 │
    ├── uPortal container          │
    │   ├── uportal binary         │
    │   ├── uportal.db (SQLite)    │
    │   ├── templates/             │
    │   └── static/                 │
    │                             │
    └── jitsi-docker stack         │
        ├── prosody (XMPP)         │
        ├── jicofo (focus)         │
        ├── jvb (video bridge)     │
        └── web (nginx front)      │
                                    │
    ┌──────────────────────────────┘
    │
    JWT Signing: uPortal signs JWT with JITSI_SECRET
    → Redirects user to meet.example.com/room?jwt=...
    → Jitsi verifies JWT with matching JWT_APP_SECRET

Troubleshooting

Symptom Likely Cause Fix
"Jitsi secret is not configured" JITSI_SECRET not set in uPortal .env Set the variable
"Jitsi app id is not configured" JITSI_APP_ID not set Set the variable
"Authentication failed" in Jitsi JWT secret mismatch between uPortal and Jitsi Verify JITSI_SECRET = JWT_APP_SECRET
"Invalid issuer" in Jitsi logs JITSI_APP_ID ≠ JWT_APP_ID Make them match
Avatar not showing in Jitsi SITE_DOMAIN not set, or avatar missing Set SITE_DOMAIN to the portal's public URL
External redirect warning page appears Jitsi domain not in TRUSTED_DOMAIINS Add the domain