Gryt

Docker Compose

Self-host Gryt with a single docker compose up — no cloning required

The fastest way to self-host Gryt. Everything runs from pre-built images published to GitHub Container Registry — no need to clone any repos or build anything.

What's included

ServiceImagePurpose
Clientghcr.io/gryt-chat/clientWeb UI (React + Nginx)
Serverghcr.io/gryt-chat/serverSignaling, chat, file uploads
SFUghcr.io/gryt-chat/sfuWebRTC media forwarding
ScyllaDBscylladb/scyllaMessage & user storage
MinIOminio/minioS3-compatible file storage

Quick start

Download the compose file and example env

mkdir gryt && cd gryt
curl -LO https://raw.githubusercontent.com/Gryt-chat/gryt/main/docker-compose.yml
curl -LO https://raw.githubusercontent.com/Gryt-chat/gryt/main/.env.example
cp .env.example .env
mkdir gryt && cd gryt
wget https://raw.githubusercontent.com/Gryt-chat/gryt/main/docker-compose.yml
wget https://raw.githubusercontent.com/Gryt-chat/gryt/main/.env.example
cp .env.example .env

Edit the .env file

Open .env in your editor and configure at minimum:

# Give your server a name
SERVER_NAME=My Gryt Server

# Set a real secret in production
JWT_SECRET=<run: openssl rand -base64 48>

# The URL browsers will use to reach the client
CORS_ORIGIN=http://localhost:3666

Start everything

docker compose up -d

Wait about 30–60 seconds for ScyllaDB to initialize, then open http://localhost:3666.

Architecture

┌─────────────────────────────────────────────────────────┐
│                     Docker Network                       │
│                                                         │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐          │
│  │  Client   │    │  Server   │    │   SFU    │          │
│  │  :3666    │───▶│  :5000    │───▶│  :5005   │          │
│  └──────────┘    └────┬─────┘    └──────────┘          │
│                       │                                  │
│              ┌────────┴────────┐                        │
│              │                 │                        │
│        ┌─────┴─────┐   ┌──────┴─────┐                  │
│        │  ScyllaDB  │   │   MinIO    │                  │
│        │  :9042     │   │   :9000    │                  │
│        └───────────┘   └────────────┘                  │
└─────────────────────────────────────────────────────────┘

Configuration reference

Image versions

Pin specific versions instead of latest for reproducible deploys:

CLIENT_VERSION=1.0.1
SERVER_VERSION=1.0.0
SFU_VERSION=1.0.0

Browse available tags at github.com/orgs/gryt-chat/packages.

Ports

VariableDefaultDescription
CLIENT_PORT3666Web UI
SERVER_PORT5000Signaling API + WebSocket
SFU_PORT5005SFU WebSocket
SFU_UDP_MIN10000WebRTC media UDP range start
SFU_UDP_MAX10019WebRTC media UDP range end

Server

VariableDefaultDescription
SERVER_NAMEMy Gryt ServerDisplay name in the server browser
SERVER_DESCRIPTIONA Gryt voice chat serverServer description
SERVER_PASSWORD(empty)Password to join (leave empty for open)
SERVER_INVITE_ONLYfalseRequire invites to join
JWT_SECRETchange-me-in-productionSession token secret
CORS_ORIGINhttp://localhost:3666Allowed origins for the API

Authentication

VariableDefaultDescription
GRYT_AUTH_MODEdisableddisabled or required
GRYT_OIDC_ISSUERhttps://auth.gryt.chat/realms/grytOIDC issuer URL
GRYT_OIDC_AUDIENCEgryt-webOIDC audience

WebRTC / NAT

VariableDefaultDescription
STUN_SERVERSGoogle STUNComma-separated STUN URIs
SFU_PUBLIC_HOSTws://localhost:5005Public SFU URL(s) for browsers (comma-separated for multi-network)
SFU_ADVERTISE_IP(auto)Public IP(s) to advertise in ICE candidates (comma-separated for multi-network)

Storage

VariableDefaultDescription
MINIO_ROOT_USERminioadminMinIO admin username
MINIO_ROOT_PASSWORDminioadminMinIO admin password
S3_BUCKETgrytBucket for file uploads

Production checklist

Before going public

  • Set a strong JWT_SECRET — generate with openssl rand -base64 48
  • Change MINIO_ROOT_USER and MINIO_ROOT_PASSWORD
  • Open UDP ports 10000-10019 in your firewall
  • Set SFU_ADVERTISE_IP if behind NAT
  • Set SFU_PUBLIC_HOST to your public wss:// URL
  • Set CORS_ORIGIN to your production domain
  • Put a reverse proxy (Caddy, Nginx, Traefik) in front for TLS
  • Pin image versions instead of latest

The simplest way to add HTTPS is Caddy — it handles certificates automatically.

Add this to your docker-compose.yml:

services:
  caddy:
    image: caddy:latest
    container_name: gryt-caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
    networks:
      - gryt
    restart: unless-stopped

volumes:
  caddy-data:

And create a Caddyfile:

gryt.example.com {
    reverse_proxy client:80
}

api.example.com {
    reverse_proxy server:5000
}

sfu.example.com {
    reverse_proxy sfu:5005
}

Then update your .env:

CORS_ORIGIN=https://gryt.example.com
SFU_PUBLIC_HOST=wss://sfu.example.com
CLIENT_PORT=8080  # Caddy takes over port 80/443

# Multi-network example (LAN party + public):
# SFU_PUBLIC_HOST=wss://sfu.example.com,ws://192.168.1.100:5005
# SFU_ADVERTISE_IP=203.0.113.10,192.168.1.100

Managing the stack

# View logs
docker compose logs -f

# View logs for a single service
docker compose logs -f server

# Restart a service after config change
docker compose restart server

# Update to latest images
docker compose pull && docker compose up -d

# Update to a specific version
SFU_VERSION=1.2.0 docker compose up -d sfu

# Stop everything
docker compose down

# Stop and remove all data (clean slate)
docker compose down -v

Health checks

All services expose health endpoints. Check the stack health:

curl http://localhost:5000/health   # server
curl http://localhost:5005/health   # sfu
curl http://localhost:3666/health   # client

Or use docker compose ps to see the health status of all containers.

Upgrading

# Pull the latest images
docker compose pull

# Recreate containers with new images
docker compose up -d

# Verify
docker compose ps

To upgrade to specific versions, edit the *_VERSION vars in .env:

CLIENT_VERSION=1.1.0
SERVER_VERSION=1.1.0
SFU_VERSION=1.1.0

Then run docker compose up -d.

Using external S3 / database

To use an external S3-compatible provider (AWS, Cloudflare R2, etc.) or an external ScyllaDB/Cassandra cluster, remove the scylla, minio, and minio-init services from docker-compose.yml and set the appropriate environment variables on the server service directly:

server:
  environment:
    # External ScyllaDB
    SCYLLA_CONTACT_POINTS: "your-scylla-host:9042"
    SCYLLA_KEYSPACE: gryt

    # External S3 (e.g. Cloudflare R2)
    S3_ENDPOINT: "https://<account-id>.r2.cloudflarestorage.com"
    S3_REGION: auto
    S3_ACCESS_KEY_ID: "<your-key>"
    S3_SECRET_ACCESS_KEY: "<your-secret>"
    S3_BUCKET: gryt
    S3_FORCE_PATH_STYLE: "false"

On this page