From 55cd7158a598c6df9f111467c55f4054b71af41b Mon Sep 17 00:00:00 2001 From: mikl Date: Mon, 13 Jul 2026 00:28:59 +0300 Subject: [PATCH 1/2] heather: fully remove jitsi (clean slate for fresh install) Removed jitsi from arion-compose.nix, caddy.nix, default.nix. Wiped on heather: containers, volumes, images, secrets, LE cert. Removed YC SG rule 10000/udp. Will reinstall from scratch following docs only. --- hosts/heather/arion-compose.nix | 140 -------------------------------- hosts/heather/caddy.nix | 21 ----- hosts/heather/default.nix | 3 - 3 files changed, 164 deletions(-) diff --git a/hosts/heather/arion-compose.nix b/hosts/heather/arion-compose.nix index a9c9c55..de30c69 100644 --- a/hosts/heather/arion-compose.nix +++ b/hosts/heather/arion-compose.nix @@ -34,21 +34,7 @@ teable-db = {}; teable-redis = {}; teable-assets = {}; - # jitsi-стек (4 сервиса: web/prosody/jicofo/jvb). Coturn НЕ входит в - # официальный compose — добавляется опционально для NAT traversal (пока без него). - # Образы на GHCR (https://github.com/jitsi/docker-jitsi-meet, release stable-11031). - jitsi-web = {}; - jitsi-prosody = {}; - jitsi-jicofo = {}; - jitsi-jvb = {}; - jitsi-transcripts = {}; }; - - # --- Custom network для jitsi с DNS aliases --- - # Контейнеры jitsi общаются по FQDN (xmpp.meet.jitsi). Alias задан в - # service.networks у prosody. `networks` — top-level опция arion (как - # `services`), не в docker-compose. Имя без точки (docker-compose constraint). - networks.meet-jitsi.driver = "bridge"; services = { # === Forgejo (git.iscg.dev) === # Первый сервис, proof-of-concept для цепочки arion→docker→Caddy. @@ -144,132 +130,6 @@ service.volumes = [ "teable-redis:/data" ]; }; - # === Jitsi Meet (jitsi.iscg.dev) === - # Установлено с нуля 2026-07-12 по официальным докам: - # https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker - # Репо: https://github.com/jitsi/docker-jitsi-meet (release stable-11031). - # Образы на GHCR: jitsi/* (JITSI_IMAGE_REPO default). - # Минимальный стек: web/prosody/jicofo/jvb. Coturn НЕ входит в официальный - # compose — подключается опционально для NAT traversal (пока без него, - # JVB на :10000/udp + STUN достаточно для большинства клиентов). - # - Data: named volumes (web/prosody/jicofo/jvb + transcripts). - # - Секреты НОВЫЕ (JICOFO_COMPONENT_SECRET, JICOFO_AUTH_PASSWORD, - # JVB_AUTH_PASSWORD) в /var/lib/jitsi-secrets/jitsi.env (root:docker 0640). - # - Caddy терминирует TLS (DISABLE_HTTPS=1), web внутренне HTTP. - # - WebSocket routes (/xmpp-websocket, /colibri-ws) — в caddy.nix. - # - JVB_ADVERTISE_IPS = 51.250.45.111 (публичный IP heather). - # - Authentication: internal (users через prosodyctl в контейнере prosody). - # Документация: https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker - jitsi-web = { - service.image = "jitsi/web:stable-11031"; - service.container_name = "jitsi-web"; - service.restart = "unless-stopped"; - service.ports = [ "127.0.0.1:8000:80" ]; - service.volumes = [ - "jitsi-web:/config" - "jitsi-transcripts:/usr/share/jitsi-meet/transcripts" - ]; - service.depends_on = [ "jitsi-jvb" ]; - service.networks = [ "meet-jitsi" ]; - service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; - service.environment = { - PUBLIC_URL = "https://jitsi.iscg.dev"; - # Reverse proxy mode (Caddy терминирует TLS) - DISABLE_HTTPS = "1"; - ENABLE_HTTP_REDIRECT = "0"; - ENABLE_HSTS = "0"; - ENABLE_LETSENCRYPT = "0"; - TZ = "Europe/Moscow"; - HTTP_PORT = "8000"; - JVB_ADVERTISE_IPS = "51.250.45.111"; - DOCKER_HOST_ADDRESS = "51.250.45.111"; - # Брендинг - APP_NAME = "mikl meet"; - PROVIDER_NAME = "mikl meet"; - SHOW_JITSI_WATERMARK = "false"; - SHOW_POWERED_BY = "false"; - DISABLE_JOIN_LEAVE_NOTIFICATIONS = "true"; - DISABLE_VIDEO_BACKGROUND = "true"; - # Authentication: internal (users через prosodyctl register) - ENABLE_AUTH = "1"; - AUTH_TYPE = "internal"; - ENABLE_GUESTS = "1"; - # WebSocket для signalling/bridge (reverse proxy friendly) - ENABLE_XMPP_WEBSOCKET = "1"; - ENABLE_COLIBRI_WEBSOCKET = "1"; - # Качество видео - RESOLUTION = "1080"; - RESOLUTION_MIN = "360"; - ENABLE_SIMULCAST = "true"; - ENABLE_P2P = "true"; - ENABLE_CODEC_VP8 = "1"; - ENABLE_CODEC_VP9 = "1"; - ENABLE_CODEC_H264 = "1"; - ENABLE_CODEC_AV1 = "0"; - }; - }; - - jitsi-prosody = { - service.image = "jitsi/prosody:stable-11031"; - service.container_name = "jitsi-prosody"; - service.restart = "unless-stopped"; - service.expose = [ "5222" "5269" "5347" "5280" ]; - service.volumes = [ "jitsi-prosody:/config" ]; - # network alias: prosody доступен как xmpp.meet.jitsi для jicofo/jvb/web - service.networks.meet-jitsi.aliases = [ "xmpp.meet.jitsi" ]; - service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; - service.environment = { - TZ = "Europe/Moscow"; - ENABLE_AUTH = "1"; - AUTH_TYPE = "internal"; - ENABLE_GUESTS = "1"; - ENABLE_XMPP_WEBSOCKET = "1"; - ENABLE_IPV6 = "0"; - PUBLIC_URL = "https://jitsi.iscg.dev"; - }; - }; - - jitsi-jicofo = { - service.image = "jitsi/jicofo:stable-11031"; - service.container_name = "jitsi-jicofo"; - service.restart = "unless-stopped"; - service.ports = [ "127.0.0.1:8888:8888" ]; - service.volumes = [ "jitsi-jicofo:/config" ]; - service.depends_on = [ "jitsi-prosody" ]; - service.networks = [ "meet-jitsi" ]; - service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; - service.environment = { - TZ = "Europe/Moscow"; - ENABLE_AUTH = "1"; - AUTH_TYPE = "internal"; - JICOFO_MAX_MEMORY = "1024m"; - MAX_PARTICIPANTS = "10"; - }; - }; - - jitsi-jvb = { - service.image = "jitsi/jvb:stable-11031"; - service.container_name = "jitsi-jvb"; - service.restart = "unless-stopped"; - service.ports = [ - "10000:10000/udp" # публично — media traffic - "127.0.0.1:8080:8080" # colibri REST (localhost only) - ]; - service.volumes = [ "jitsi-jvb:/config" ]; - service.depends_on = [ "jitsi-prosody" ]; - service.networks = [ "meet-jitsi" ]; - service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; - service.environment = { - TZ = "Europe/Moscow"; - PUBLIC_URL = "https://jitsi.iscg.dev"; - JVB_ADVERTISE_IPS = "51.250.45.111"; - JVB_PORT = "10000"; - JVB_DISABLE_STUN = "0"; - VIDEOBRIDGE_MAX_MEMORY = "2048m"; - DOCKER_HOST_ADDRESS = "51.250.45.111"; - }; - }; - # === Silverbullet (note.iscg.dev) — ИСКЛЮЧЕНИЕ: bind mount === # Space = /srv/syncthing/herbarium (sync'ается syncthing на устройства). # Единственный сервис без named volume — по твоему решению, чтобы notes diff --git a/hosts/heather/caddy.nix b/hosts/heather/caddy.nix index 71b2329..0ad6ec1 100644 --- a/hosts/heather/caddy.nix +++ b/hosts/heather/caddy.nix @@ -73,27 +73,6 @@ in { encode zstd gzip ''; - # Jitsi Meet (web + WebSocket routes) - # Доки: https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#running-behind-a-reverse-proxy - # WebSocket routes (/xmpp-websocket, /colibri-ws) — hop-by-hop, требуют - # явного Upgrade/Connection. Остальное → web контейнер на 127.0.0.1:8000. - "jitsi.iscg.dev".extraConfig = '' - @xmpp path /xmpp-websocket* - reverse_proxy @xmpp 127.0.0.1:8000 { - header_up Host {host} - header_up Upgrade {http.upgrade} - header_up Connection {http.connection} - } - @colibri path /colibri-ws* - reverse_proxy @colibri 127.0.0.1:8000 { - header_up Host {host} - header_up Upgrade {http.upgrade} - header_up Connection {http.connection} - } - reverse_proxy 127.0.0.1:8000 - encode zstd gzip - ''; - # Teable "teable.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:2345 diff --git a/hosts/heather/default.nix b/hosts/heather/default.nix index 7e97c12..1f0011b 100644 --- a/hosts/heather/default.nix +++ b/hosts/heather/default.nix @@ -43,12 +43,9 @@ 22000 # syncthing sync 22067 # strelaysrv (relay) 22070 # strelaysrv (status) - # jitsi: 3478/5349 (turn, когда добавим coturn) ]; firewall.allowedUDPPorts = [ 22000 # syncthing sync (QUIC) - 10000 # jitsi jvb (RTP media) - # jitsi: 49152-65535 (RTP range, когда добавим coturn через allowedUDPPortRanges) ]; }; # useDHCP задаёт ./image.nix (mkDefault) — облачный профиль. From cd018a24c23fb2e2c85037fae7b152483f442ba0 Mon Sep 17 00:00:00 2001 From: mikl Date: Mon, 13 Jul 2026 00:56:13 +0300 Subject: [PATCH 2/2] heather: install jitsi from scratch per official docs (minimal) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker and official docker-compose.yml. Minimal config, no copying from old install. - 4 services: web/prosody/jicofo/jvb (stable-11031, docker hub) - read_only + tmpfs per official compose - Named volumes: jitsi-{web,prosody,prosody-data,jicofo,jvb,transcripts} - Network meet-jitsi, prosody alias xmpp.meet.jitsi - Reverse proxy: DISABLE_HTTPS=1, Caddy v2 auto-WebSocket (no manual header_up) - JVB_ADVERTISE_IPS=51.250.45.111, :10000/udp public - Auth: internal (ENABLE_AUTH=1, AUTH_TYPE=internal). NO GUESTS — template bug in prosody: guest domain lacks websocket module → disconnect loop - Secrets NEW in /var/lib/jitsi-secrets/jitsi.env (root:docker 0640): JICOFO_COMPONENT_SECRET, JICOFO_AUTH_PASSWORD, JVB_AUTH_PASSWORD - Firewall: 10000/udp (NixOS + YC SG) --- hosts/heather/arion-compose.nix | 130 ++++++++++++++++++++++++++++++++ hosts/heather/caddy.nix | 6 ++ hosts/heather/default.nix | 1 + 3 files changed, 137 insertions(+) diff --git a/hosts/heather/arion-compose.nix b/hosts/heather/arion-compose.nix index de30c69..cbc7128 100644 --- a/hosts/heather/arion-compose.nix +++ b/hosts/heather/arion-compose.nix @@ -34,7 +34,17 @@ teable-db = {}; teable-redis = {}; teable-assets = {}; + # jitsi-стек (minimal, per official docker-compose.yml) + jitsi-web-config = {}; + jitsi-prosody-config = {}; + jitsi-prosody-data = {}; + jitsi-jicofo-config = {}; + jitsi-jvb-config = {}; + jitsi-transcripts = {}; }; + + # Custom network for jitsi (FQDN-based comms, prosody alias xmpp.meet.jitsi) + networks.meet-jitsi.driver = "bridge"; services = { # === Forgejo (git.iscg.dev) === # Первый сервис, proof-of-concept для цепочки arion→docker→Caddy. @@ -130,6 +140,126 @@ service.volumes = [ "teable-redis:/data" ]; }; + # === Jitsi Meet (jitsi.iscg.dev) === + # Installed from scratch 2026-07-13 per official docs: + # https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker + # Repo: https://github.com/jitsi/docker-jitsi-meet (release stable-11031) + # Minimal stack: web/prosody/jicofo/jvb. No coturn/jigasi/jibri. + # - Images: jitsi/* (docker hub; GHCR ghcr.io/jitsi/* not accessible) + # - read_only + tmpfs per official compose + # - Named volumes (config + prosody data separate) + # - Reverse proxy: DISABLE_HTTPS=1, Caddy terminates TLS + # - JVB_ADVERTISE_IPS=51.250.45.111, :10000/udp public + # - Auth: internal (ENABLE_AUTH=1, AUTH_TYPE=internal). No GUESTS + # (template bug: guest domain lacks websocket module → disconnect). + # - Secrets in /var/lib/jitsi-secrets/jitsi.env (root:docker 0640) + jitsi-web = { + service.image = "jitsi/web:stable-11031"; + service.container_name = "jitsi-web"; + service.restart = "unless-stopped"; + service.read_only = true; + service.tmpfs = [ + "/run:size=16M,mode=1750,exec" + "/tmp:size=16M,mode=1777,noexec" + ]; + service.ports = [ "127.0.0.1:8000:80" ]; + service.volumes = [ + "jitsi-web-config:/config" + "jitsi-transcripts:/usr/share/jitsi-meet/transcripts" + ]; + service.depends_on = [ "jitsi-jvb" ]; + service.networks = [ "meet-jitsi" ]; + service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; + service.environment = { + PUBLIC_URL = "https://jitsi.iscg.dev"; + TZ = "Europe/Moscow"; + HTTP_PORT = "8000"; + DISABLE_HTTPS = "1"; + ENABLE_HTTP_REDIRECT = "0"; + ENABLE_HSTS = "0"; + ENABLE_LETSENCRYPT = "0"; + JVB_ADVERTISE_IPS = "51.250.45.111"; + DOCKER_HOST_ADDRESS = "51.250.45.111"; + ENABLE_AUTH = "1"; + AUTH_TYPE = "internal"; + ENABLE_XMPP_WEBSOCKET = "1"; + ENABLE_COLIBRI_WEBSOCKET = "1"; + }; + }; + + jitsi-prosody = { + service.image = "jitsi/prosody:stable-11031"; + service.container_name = "jitsi-prosody"; + service.restart = "unless-stopped"; + service.read_only = true; + service.tmpfs = [ + "/run:size=16M,mode=1750,exec" + "/tmp:size=16M,mode=1777,noexec" + ]; + service.expose = [ "5222" "5269" "5347" "5280" ]; + service.volumes = [ + "jitsi-prosody-config:/config" + "jitsi-prosody-data:/var/lib/prosody" + ]; + service.networks.meet-jitsi.aliases = [ "xmpp.meet.jitsi" ]; + service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; + service.environment = { + TZ = "Europe/Moscow"; + PUBLIC_URL = "https://jitsi.iscg.dev"; + ENABLE_AUTH = "1"; + AUTH_TYPE = "internal"; + ENABLE_XMPP_WEBSOCKET = "1"; + ENABLE_IPV6 = "0"; + }; + }; + + jitsi-jicofo = { + service.image = "jitsi/jicofo:stable-11031"; + service.container_name = "jitsi-jicofo"; + service.restart = "unless-stopped"; + service.read_only = true; + service.tmpfs = [ + "/run:size=16M,mode=1750,exec" + "/tmp:size=16M,mode=1777,noexec" + ]; + service.ports = [ "127.0.0.1:8888:8888" ]; + service.volumes = [ "jitsi-jicofo-config:/config" ]; + service.depends_on = [ "jitsi-prosody" ]; + service.networks = [ "meet-jitsi" ]; + service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; + service.environment = { + TZ = "Europe/Moscow"; + ENABLE_AUTH = "1"; + AUTH_TYPE = "internal"; + }; + }; + + jitsi-jvb = { + service.image = "jitsi/jvb:stable-11031"; + service.container_name = "jitsi-jvb"; + service.restart = "unless-stopped"; + service.read_only = true; + service.tmpfs = [ + "/run:size=16M,mode=1750,exec" + "/tmp:size=16M,mode=1777,noexec" + ]; + service.ports = [ + "10000:10000/udp" + "127.0.0.1:8080:8080" + ]; + service.volumes = [ "jitsi-jvb-config:/config" ]; + service.depends_on = [ "jitsi-prosody" ]; + service.networks = [ "meet-jitsi" ]; + service.env_file = [ "/var/lib/jitsi-secrets/jitsi.env" ]; + service.environment = { + TZ = "Europe/Moscow"; + PUBLIC_URL = "https://jitsi.iscg.dev"; + JVB_ADVERTISE_IPS = "51.250.45.111"; + JVB_PORT = "10000"; + DOCKER_HOST_ADDRESS = "51.250.45.111"; + }; + }; + # === Silverbullet (note.iscg.dev) — ИСКЛЮЧЕНИЕ: bind mount === # Space = /srv/syncthing/herbarium (sync'ается syncthing на устройства). # Единственный сервис без named volume — по твоему решению, чтобы notes diff --git a/hosts/heather/caddy.nix b/hosts/heather/caddy.nix index 0ad6ec1..4caccaf 100644 --- a/hosts/heather/caddy.nix +++ b/hosts/heather/caddy.nix @@ -73,6 +73,12 @@ in { encode zstd gzip ''; + # Jitsi Meet (Caddy v2 auto-handles WebSocket upgrade) + # Docs: https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#running-behind-a-reverse-proxy + "jitsi.iscg.dev".extraConfig = '' + reverse_proxy 127.0.0.1:8000 + ''; + # Teable "teable.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:2345 diff --git a/hosts/heather/default.nix b/hosts/heather/default.nix index 1f0011b..818ff3f 100644 --- a/hosts/heather/default.nix +++ b/hosts/heather/default.nix @@ -46,6 +46,7 @@ ]; firewall.allowedUDPPorts = [ 22000 # syncthing sync (QUIC) + 10000 # jitsi jvb (RTP media) ]; }; # useDHCP задаёт ./image.nix (mkDefault) — облачный профиль.