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) — облачный профиль.