From ce96105463987a00251665223b4dc595dcbc1e6e Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 20 Oct 2026 15:34:05 +0200 Subject: [PATCH] Make Jitsi JVB work correctly with IPv6 Since the Jitsi Docker Compose stack does not properly take IPv6 into consideration, patch the JVB config to add mappings from the private IPv6 address of the Docker network to the public IPv6 address. This should be the source of the reported problems with conferences with 3+ people. --- roles/jitsi/README.md | 8 +++++++- roles/jitsi/defaults/main.yaml | 2 ++ roles/jitsi/handlers/main.yaml | 10 ++++++++++ roles/jitsi/meta/argument_specs.yaml | 8 ++++++++ roles/jitsi/tasks/main.yaml | 19 +++++++++++++++++++ .../jitsi/templates/compose.override.yaml.j2 | 14 +++++++++++++- roles/jitsi/templates/custom-jvb.conf.j2 | 11 +++++++++++ roles/jitsi/templates/jitsi.env.j2 | 4 ++-- roles/jitsi/vars/main.yaml | 4 ++++ 9 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 roles/jitsi/templates/custom-jvb.conf.j2 diff --git a/roles/jitsi/README.md b/roles/jitsi/README.md index 57f5f8e..b3392c6 100644 --- a/roles/jitsi/README.md +++ b/roles/jitsi/README.md @@ -35,8 +35,14 @@ Should work on Debian-based distributions. - `jitsi__timezone`: The time zone for the containers. Defaults to `Europe/Berlin`. -- `jitsi__jvb_advertise_ips`: List of IP addresses the JVB should advertise for media (`JVB_ADVERTISE_IPS`). +- `jitsi__jvb_advertise_ips`: List of IP addresses the JVB should advertise for media. + IPv4 addresses are put into `JVB_ADVERTISE_IPS`, which docker-jitsi-meet maps to the IPv4 address of the JVB container. + IPv6 addresses are mapped to the IPv6 address of the JVB container in `custom-jvb.conf` instead, for which the `meet.jitsi` network gets a fixed IPv6 subnet and the JVB a fixed IPv6 address. Defaults to `[ ]`. +- `jitsi__ipv6_subnet`: The IPv6 subnet of the `meet.jitsi` network, if IPv6 addresses are advertised. + Defaults to `fd4a:7c1e:93b5:1::/64`. +- `jitsi__jvb_ipv6_address`: The IPv6 address of the JVB container within `jitsi__ipv6_subnet`. + Defaults to `fd4a:7c1e:93b5:1::10`. - `jitsi__whiteboard`: Whether to deploy the Excalidraw whiteboard backend (`whiteboard.yml`). Defaults to `false`. - `jitsi__turn`: Whether to deploy coturn and configure Jitsi to use it. diff --git a/roles/jitsi/defaults/main.yaml b/roles/jitsi/defaults/main.yaml index 5bed993..8532b27 100644 --- a/roles/jitsi/defaults/main.yaml +++ b/roles/jitsi/defaults/main.yaml @@ -1,5 +1,7 @@ jitsi__timezone: Europe/Berlin jitsi__jvb_advertise_ips: [ ] +jitsi__ipv6_subnet: fd4a:7c1e:93b5:1::/64 +jitsi__jvb_ipv6_address: fd4a:7c1e:93b5:1::10 jitsi__whiteboard: false jitsi__turn: false jitsi__prometheus_exporter: false diff --git a/roles/jitsi/handlers/main.yaml b/roles/jitsi/handlers/main.yaml index 7bde7f0..6f0eb2e 100644 --- a/roles/jitsi/handlers/main.yaml +++ b/roles/jitsi/handlers/main.yaml @@ -8,6 +8,16 @@ state: restarted become: true +- name: restart jitsi jvb + community.docker.docker_compose_v2: + project_src: "{{ jitsi__base_dir }}" + project_name: jitsi + files: "{{ jitsi__compose_files }}" + services: + - jvb + state: restarted + become: true + - name: restart jitsi coturn community.docker.docker_compose_v2: project_src: "{{ jitsi__base_dir }}" diff --git a/roles/jitsi/meta/argument_specs.yaml b/roles/jitsi/meta/argument_specs.yaml index 36ee25b..e2df54f 100644 --- a/roles/jitsi/meta/argument_specs.yaml +++ b/roles/jitsi/meta/argument_specs.yaml @@ -27,6 +27,14 @@ argument_specs: elements: str required: false default: [ ] + jitsi__ipv6_subnet: + type: str + required: false + default: fd4a:7c1e:93b5:1::/64 + jitsi__jvb_ipv6_address: + type: str + required: false + default: fd4a:7c1e:93b5:1::10 jitsi__whiteboard: type: bool required: false diff --git a/roles/jitsi/tasks/main.yaml b/roles/jitsi/tasks/main.yaml index 92b9da8..4846ad5 100644 --- a/roles/jitsi/tasks/main.yaml +++ b/roles/jitsi/tasks/main.yaml @@ -110,6 +110,25 @@ label: "{{ item.name }}" notify: restart jitsi web +- name: deploy custom JVB configuration + ansible.builtin.template: + src: custom-jvb.conf.j2 + dest: "{{ jitsi__config_dir }}/jvb/custom-jvb.conf" + mode: "0644" + owner: root + group: root + become: true + when: jitsi__jvb_advertise_ipv6s | length > 0 + notify: restart jitsi jvb + +- name: remove unconfigured custom JVB configuration + ansible.builtin.file: + path: "{{ jitsi__config_dir }}/jvb/custom-jvb.conf" + state: absent + become: true + when: jitsi__jvb_advertise_ipv6s | length == 0 + notify: restart jitsi jvb + - name: make sure the coturn directory exists ansible.builtin.file: path: "{{ jitsi__base_dir }}/coturn" diff --git a/roles/jitsi/templates/compose.override.yaml.j2 b/roles/jitsi/templates/compose.override.yaml.j2 index 628b83b..2cefd13 100644 --- a/roles/jitsi/templates/compose.override.yaml.j2 +++ b/roles/jitsi/templates/compose.override.yaml.j2 @@ -6,8 +6,20 @@ networks: meet.jitsi: enable_ipv6: true +{% if jitsi__jvb_advertise_ipv6s | length > 0 %} + # A fixed subnet, so the JVB can have a fixed IPv6 address to map the public ones to. + ipam: + config: + - subnet: {{ jitsi__ipv6_subnet }} +{% endif %} services: +{% if jitsi__jvb_advertise_ipv6s | length > 0 %} + jvb: + networks: + meet.jitsi: + ipv6_address: {{ jitsi__jvb_ipv6_address }} +{% endif %} {% if jitsi__web_files | length > 0 %} web: volumes: @@ -42,6 +54,6 @@ services: networks: meet.jitsi: {% endif %} -{% if not (jitsi__web_files or jitsi__turn or jitsi__prometheus_exporter) %} +{% if not (jitsi__jvb_advertise_ipv6s or jitsi__web_files or jitsi__turn or jitsi__prometheus_exporter) %} {} {% endif %} diff --git a/roles/jitsi/templates/custom-jvb.conf.j2 b/roles/jitsi/templates/custom-jvb.conf.j2 new file mode 100644 index 0000000..050511a --- /dev/null +++ b/roles/jitsi/templates/custom-jvb.conf.j2 @@ -0,0 +1,11 @@ +# {{ ansible_managed }} +# Included at the end of the generated jvb.conf. +# Map the advertised IPv6 addresses to the pinned IPv6 address of the JVB, in addition to +# the mappings of the IPv4 addresses generated from JVB_ADVERTISE_IPS. +{% for ip in jitsi__jvb_advertise_ipv6s %} +ice4j.harvest.mapping.static-mappings += { + local-address = "{{ jitsi__jvb_ipv6_address }}" + public-address = "{{ ip }}" + name = "ipv6-{{ loop.index0 }}" +} +{% endfor %} diff --git a/roles/jitsi/templates/jitsi.env.j2 b/roles/jitsi/templates/jitsi.env.j2 index f09b041..f5d8a44 100644 --- a/roles/jitsi/templates/jitsi.env.j2 +++ b/roles/jitsi/templates/jitsi.env.j2 @@ -19,8 +19,8 @@ ENABLE_LETSENCRYPT=1 LETSENCRYPT_DOMAIN={{ jitsi__domain }} LETSENCRYPT_EMAIL={{ jitsi__letsencrypt_email }} LETSENCRYPT_ACME_SERVER=letsencrypt -{% if jitsi__jvb_advertise_ips | length > 0 %} -JVB_ADVERTISE_IPS={{ jitsi__jvb_advertise_ips | join(',') }} +{% if jitsi__jvb_advertise_ipv4s | length > 0 %} +JVB_ADVERTISE_IPS={{ jitsi__jvb_advertise_ipv4s | join(',') }} {% endif %} {% if jitsi__whiteboard %} diff --git a/roles/jitsi/vars/main.yaml b/roles/jitsi/vars/main.yaml index 21fe76b..993aa41 100644 --- a/roles/jitsi/vars/main.yaml +++ b/roles/jitsi/vars/main.yaml @@ -1,4 +1,8 @@ jitsi__base_dir: /opt/jitsi jitsi__config_dir: "{{ jitsi__base_dir }}/config" jitsi__release_dir: "{{ jitsi__base_dir }}/releases/docker-jitsi-meet-{{ jitsi__version }}" +# docker-jitsi-meet maps all JVB_ADVERTISE_IPS to the container's IPv4 address, so IPv6 +# addresses are mapped to the pinned IPv6 address of the JVB in custom-jvb.conf instead. +jitsi__jvb_advertise_ipv4s: "{{ jitsi__jvb_advertise_ips | reject('search', ':') | list }}" +jitsi__jvb_advertise_ipv6s: "{{ jitsi__jvb_advertise_ips | select('search', ':') | list }}" jitsi__compose_files: "{{ ['release/docker-compose.yml'] + (['release/whiteboard.yml'] if jitsi__whiteboard else []) + ['compose.override.yaml'] }}"