{"meta":{"slug":"pterodactyl-egg-gryt-server","name":"Pterodactyl Egg Gryt Server","category":"gryt","source":"pelican","owner":"pelican-eggs","repo":"voice","branch":"main","path":"gryt/pterodactyl-egg-gryt-server.json","rawUrl":"https://raw.githubusercontent.com/pelican-eggs/voice/main/gryt/pterodactyl-egg-gryt-server.json","indexedAt":1792246788483},"egg":{"_comment":"DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO","meta":{"version":"PTDL_v2","update_url":null},"exported_at":"2026-10-05T18:39:22+00:00","name":"Gryt Server","author":"138704619+wan0v@users.noreply.github.com","description":"Gryt is an open source voice, video and text chat platform. Connect with the Gryt desktop app or the hosted web client at https://app.gryt.chat. The first user to join becomes the owner; afterwards the server is invite-only. Requirements: - amd64 only (the Gryt Linux bundle is x64 only) - needs two extra allocations: one for the SFU websocket (set SFU_PORT to its port), one for voice media (set ICE_UDP_MUX_PORT to its port) - set SFU_PUBLIC_HOST to <public-ip>:<SFU_PORT> so voice works over the internet","features":[],"docker_images":{"Node.js 24":"ghcr.io/pelican-eggs/yolks:nodejs_24"},"file_denylist":[],"startup":"export SERVER_VERSION=\"$(node -p \"require(process.argv[1]).version\" ./server/package.json)\"; if [ -z \"$JWT_SECRET\" ]; then [ -f .jwt_secret ] || head -c 48 /dev/urandom | base64 > .jwt_secret; export JWT_SECRET=\"$(cat .jwt_secret)\"; fi; ./gryt_sfu & if [ \"$IMAGE_WORKER_ENABLED\" != \"false\" ]; then node --env-file=config.env image-worker/dist/index.js & fi; exec node --env-file=config.env server/dist/index.js","config":{"files":"{\n    \"config.env\": {\n        \"parser\": \"properties\",\n        \"find\": {\n            \"SERVER_NAME\": \"{{server.build.env.SERVER_NAME}}\",\n            \"SERVER_DESCRIPTION\": \"{{server.build.env.SERVER_DESCRIPTION}}\",\n            \"HOST\": \"0.0.0.0\",\n            \"PORT\": \"{{server.build.default.port}}\",\n            \"DATA_DIR\": \"./data\",\n            \"STORAGE_BACKEND\": \"filesystem\",\n            \"S3_BUCKET\": \"uploads\",\n            \"JWT_SECRET\": \"{{server.build.env.JWT_SECRET}}\",\n            \"GRYT_IDENTITY_TIERS\": \"{{server.build.env.GRYT_IDENTITY_TIERS}}\",\n            \"EXTERNAL_HOST\": \"{{server.build.env.EXTERNAL_HOST}}\",\n            \"SFU_PORT\": \"{{server.build.env.SFU_PORT}}\",\n            \"SFU_WS_HOST\": \"ws://127.0.0.1:{{server.build.env.SFU_PORT}}\",\n            \"SFU_PUBLIC_HOST\": \"{{server.build.env.SFU_PUBLIC_HOST}}\",\n            \"STUN_SERVERS\": \"{{server.build.env.STUN_SERVERS}}\",\n            \"CORS_ORIGIN\": \"{{server.build.env.CORS_ORIGIN}}\"\n        }\n    }\n}","startup":"{\n    \"done\": \"Signaling server started at\"\n}","logs":"{}","stop":"^C"},"scripts":{"installation":{"script":"#!/bin/bash\n# Gryt Server (self-hosted Linux bundle) installation script\n\nset -e\n\ncd /mnt/server\n\n## The Gryt Linux bundle is x86_64 only\nif [ \"$(uname -m)\" != \"x86_64\" ]; then\n    echo \"ERROR: This egg requires an x86_64 (amd64) node, this one is $(uname -m).\"\n    echo \"Gryt only publishes x64 self-hosted bundles (the gryt_sfu binary cannot run here).\"\n    exit 1\nfi\n\n## Resolve the release to install\nif [ -z \"${GRYT_VERSION}\" ] || [ \"${GRYT_VERSION}\" = \"latest\" ]; then\n    echo \"Resolving latest Gryt release...\"\n    RELEASE_JSON=$(curl -sSL https://api.github.com/repos/Gryt-chat/server/releases/latest)\n    RESOLVED_VERSION=$(echo \"${RELEASE_JSON}\" | jq -r '.tag_name | ltrimstr(\"v\")')\n    DOWNLOAD_URL=$(echo \"${RELEASE_JSON}\" | jq -r '.assets[] | select(.name | test(\"^gryt-server-linux-x64-v[0-9].*\\\\.zip$\")) | .browser_download_url')\nelse\n    RESOLVED_VERSION=\"${GRYT_VERSION#v}\"\n    DOWNLOAD_URL=\"https://github.com/Gryt-chat/server/releases/download/v${RESOLVED_VERSION}/gryt-server-linux-x64-v${RESOLVED_VERSION}.zip\"\nfi\n\nif [ -z \"${RESOLVED_VERSION}\" ] || [ \"${RESOLVED_VERSION}\" = \"null\" ] || [ -z \"${DOWNLOAD_URL}\" ] || [ \"${DOWNLOAD_URL}\" = \"null\" ]; then\n    echo \"ERROR: Could not resolve a Gryt release for version '${GRYT_VERSION}'.\"\n    exit 1\nfi\n\necho \"Installing Gryt Server v${RESOLVED_VERSION} (linux-x64 bundle)\"\n\n## Download and extract the bundle\necho \"Downloading ${DOWNLOAD_URL} ...\"\ncurl -sSL --fail -o /mnt/server/gryt.zip \"${DOWNLOAD_URL}\"\n\nunzip -q -o /mnt/server/gryt.zip -d /mnt/server\ncp -a /mnt/server/gryt-server-linux-x64/. /mnt/server/\nrm -rf /mnt/server/gryt-server-linux-x64 /mnt/server/gryt.zip\n\n## Unused bundle wrappers (the egg starts node/gryt_sfu directly)\nrm -f /mnt/server/start.bat /mnt/server/gryt_server.bat /mnt/server/gryt_image_worker.bat \\\n      /mnt/server/gryt_server.sh /mnt/server/gryt_image_worker.sh\n\nif [ ! -f /mnt/server/server/dist/index.js ] || [ ! -f /mnt/server/gryt_sfu ]; then\n    echo \"ERROR: Bundle seems incomplete (server/dist/index.js or gryt_sfu missing).\"\n    exit 1\nfi\n\nchmod +x /mnt/server/gryt_sfu\nmkdir -p /mnt/server/data\n\necho \"Installation of Gryt Server v${RESOLVED_VERSION} complete.\"\nexit 0","container":"ghcr.io/pelican-eggs/installers:debian","entrypoint":"bash"}},"variables":[{"name":"Gryt Version","description":"The Gryt Server version to install (e.g. 1.6.5 or latest). Reinstall the server to apply a change.","env_variable":"GRYT_VERSION","default_value":"latest","user_viewable":true,"user_editable":true,"rules":"required|string|max:32","field_type":"text"},{"name":"Server Name","description":"Display name shown to users.","env_variable":"SERVER_NAME","default_value":"My Gryt Server","user_viewable":true,"user_editable":true,"rules":"required|string|max:64","field_type":"text"},{"name":"Server Description","description":"Description of the server.","env_variable":"SERVER_DESCRIPTION","default_value":"A Gryt voice chat server","user_viewable":true,"user_editable":true,"rules":"nullable|string|max:255","field_type":"text"},{"name":"JWT Secret","description":"Secret for session tokens. Leave empty to auto-generate a persistent one on first start.","env_variable":"JWT_SECRET","default_value":"","user_viewable":false,"user_editable":false,"rules":"nullable|string|min:32|max:128","field_type":"text"},{"name":"SFU Port","description":"TCP port of the voice SFU websocket. Must match an extra port allocation.","env_variable":"SFU_PORT","default_value":"5005","user_viewable":true,"user_editable":true,"rules":"required|numeric|between:1024,65535","field_type":"text"},{"name":"SFU Public Host","description":"Public address of the SFU given to clients, e.g. 203.0.113.10:5005 or sfu.example.com:5005 (wss://... behind a TLS proxy only!). Required for voice over the internet.","env_variable":"SFU_PUBLIC_HOST","default_value":"","user_viewable":true,"user_editable":true,"rules":"nullable|string|max:255","field_type":"text"},{"name":"Voice Media Port (UDP)","description":"The one UDP port all WebRTC media flows over (ICE_UDP_MUX_PORT). Must match an extra UDP port allocation.","env_variable":"ICE_UDP_MUX_PORT","default_value":"3478","user_viewable":true,"user_editable":true,"rules":"required|numeric|between:1024,65535","field_type":"text"},{"name":"ICE Advertise IP","description":"Public IP(s) advertised in ICE candidates, comma-separated (e.g. 203.0.113.10, 203.0.113.11). Leave empty if STUN auto-discovery works. Set your public IP if the server runs behind NAT or voice does not work.","env_variable":"ICE_ADVERTISE_IP","default_value":"","user_viewable":true,"user_editable":true,"rules":"nullable|string|max:255","field_type":"text"},{"name":"Max Voice Peers","description":"How many peers may be connected to the SFU at once.","env_variable":"MAX_PEERS","default_value":"200","user_viewable":true,"user_editable":true,"rules":"required|numeric|min:1|max:10000","field_type":"text"},{"name":"STUN Servers","description":"Comma-separated STUN URIs for NAT traversal.","env_variable":"STUN_SERVERS","default_value":"stun:stun.l.google.com:19302,stun:stun1.l.google.com:19302","user_viewable":true,"user_editable":true,"rules":"required|string|max:255","field_type":"text"},{"name":"CORS Origins","description":"Allowed origins, comma-separated. Keep http://127.0.0.1:15738 (desktop app) and https://app.gryt.chat (hosted web client).","env_variable":"CORS_ORIGIN","default_value":"http://127.0.0.1:15738,https://app.gryt.chat","user_viewable":true,"user_editable":true,"rules":"required|string|max:512","field_type":"text"},{"name":"Identity Tiers","description":"Comma-separated identity types the server accepts: account (Gryt account) and/or local (no account).","env_variable":"GRYT_IDENTITY_TIERS","default_value":"account","user_viewable":true,"user_editable":true,"rules":"required|string|max:64","field_type":"text"},{"name":"External Host","description":"URL clients use to reach this server, e.g. http://203.0.113.10:5000","env_variable":"EXTERNAL_HOST","default_value":"","user_viewable":true,"user_editable":true,"rules":"nullable|string|max:255","field_type":"text"},{"name":"Image Worker","description":"Run the image worker (thumbnails, recompression, voice tile colors). true or false.","env_variable":"IMAGE_WORKER_ENABLED","default_value":"true","user_viewable":true,"user_editable":true,"rules":"required|string|in:true,false","field_type":"text"},{"name":"Disable STUN","description":"Stop the SFU from discovering its public IP via STUN. Only use with a direct, port-preserving path to the internet and set ICE_ADVERTISE_IP instead. true or false.","env_variable":"DISABLE_STUN","default_value":"false","user_viewable":true,"user_editable":true,"rules":"required|string|in:true,false","field_type":"text"}]},"readme":"# Gryt\n\n[Gryt](https://gryt.chat) is an open source voice, video and text chat platform.\n\nConnect with the [Gryt desktop app](https://github.com/Gryt-chat/gryt/releases) or the hosted web client at\n[app.gryt.chat](https://app.gryt.chat). The first user to join a new server becomes its owner; afterwards the\nserver is invite-only (create invites under **Server settings → Invites**).\n\n## Requirements\n\n- **amd64 only** — the Gryt Linux bundle is published for x64 only.\n- **3 port allocations** on the server (see below).\n\n## Server Ports\n\n| Allocation | Default | Proto | Variable | What it is |\n|------------|---------|-------|----------|------------|\n| default    | 5000    | TCP   | — (allocation port is used automatically) | Signaling server: chat, logins, everything except voice |\n| extra #1   | 5005    | TCP   | `SFU_PORT` | SFU WebSocket (voice signaling) |\n| extra #2   | 3478    | UDP   | `ICE_UDP_MUX_PORT` | WebRTC media — the actual voice/video |\n\nAfter adding the two extra allocations to the server, set `SFU_PORT` and `ICE_UDP_MUX_PORT` to their port\nnumbers in the Startup tab.\n\n## Required for voice over the internet\n\nSet **SFU Public Host** (`SFU_PUBLIC_HOST`) to the address clients use to reach the SFU, e.g.\n`203.0.113.10:5005` (or `wss://sfu.example.com` behind a TLS reverse proxy). Without it, chat works but\nvoice does not.\n\n`ICE_ADVERTISE_IP` can usually stay empty — the SFU discovers its public IP via STUN. Set it explicitly when\nthe server is behind NAT and STUN picks the wrong address.\n\n## Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `GRYT_VERSION` | `latest` | Version to install (e.g. `1.6.5`). Reinstall the server to apply. |\n| `SERVER_NAME` | `My Gryt Server` | Display name shown to users |\n| `SERVER_DESCRIPTION` | `A Gryt voice chat server` | Server description |\n| `JWT_SECRET` | *(empty)* | Session token secret. Empty = auto-generated and persisted on first start |\n| `SFU_PORT` | `5005` | TCP port of the SFU websocket — must match extra allocation #1 |\n| `SFU_PUBLIC_HOST` | *(empty)* | Public SFU address given to clients (see above) |\n| `ICE_UDP_MUX_PORT` | `3478` | UDP port all WebRTC media flows over — must match extra allocation #2 |\n| `ICE_ADVERTISE_IP` | *(empty)* | Public IP(s) advertised in ICE candidates; may be required behind when voice does not work |\n| `MAX_PEERS` | `200` | Max peers connected to the SFU at once |\n| `STUN_SERVERS` | Google STUN | Comma-separated STUN URIs |\n| `CORS_ORIGIN` | `http://127.0.0.1:15738,https://app.gryt.chat` | Allowed origins (desktop app + hosted web client) |\n| `GRYT_IDENTITY_TIERS` | `account` | Add `local` to let people join without a Gryt account |\n| `EXTERNAL_HOST` | *(empty)* | URL clients use to reach this server, e.g. `http://203.0.113.10:5000` |\n| `IMAGE_WORKER_ENABLED` | `true` | Thumbnails, recompression and voice tile colors |\n| `DISABLE_STUN` | `false` | Disable server-side STUN. Only with a direct, port-preserving path to the internet; set `ICE_ADVERTISE_IP` instead |\n\n## Voice troubleshooting\n\nIf chat works but voice does not, check all three allocations:\n\n- `SFU_PORT` must be reachable over TCP.\n- `ICE_UDP_MUX_PORT` must be reachable over UDP.\n- Both values must match the corresponding allocations.\n\nIf the desktop client cannot reach the voice server, or users connect without audio, set `ICE_ADVERTISE_IP` to the public IP through which the UDP media port is reachable. Do not use `127.0.0.1`, `0.0.0.0`, or a private container/LAN address. `DISABLE_STUN` should normally remain `false`.\n\n## Updating\n\nSet `GRYT_VERSION` and reinstall the server (or leave it on `latest` and reinstall). The `data/` directory\n(SQLite database + uploads) and the auto-generated JWT secret (`.jwt_secret`) are kept, because the install\nscript only replaces the program files.\n\n## Data\n\nAll server data lives in `data/` (`gryt.db` + `uploads/`). Back up the server by copying that directory\nwhile the server is stopped.\n"}