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.
This commit is contained in:
Stefan Bethke 2026-10-20 15:34:05 +02:00
commit ce96105463
9 changed files with 76 additions and 4 deletions

View file

@ -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.

View file

@ -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

View file

@ -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 }}"

View file

@ -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

View file

@ -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"

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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'] }}"