Weisser Zwerg Logo

Weisser Zwerg

Private Video Conferencing with Jitsi Meet behind Traefik as a Reverse Proxy

Published by Weisser Zwerg Blog on

Reclaiming your privacy by using your private Jitsi Meet video conferencing instance.

Rationale

This post is part of the Digital Civil Rights and Privacy series. In this guide, you’ll learn how to set up your own Jitsi Meet video conferencing server, paired with Traefik as a reverse proxy. By hosting your own instance, you can ensure your video calls remain private and secure, giving you full control over your data.

Prefer visual guidance? Check out the YouTube video Replace Zoom & Teams with Jitsi Meet. It’s Free, Self-Hosted, and Private! by Jim’s Garage for a step-by-step walkthrough.

Here are some helpful additional resources:

  • meet.jit.si: A free, public Jitsi Meet instance for state-of-the-art video conferencing if you’d rather not host your own.
  • Jitsi Meet on GitHub: A secure, simple, and scalable video conferencing solution you can use as a standalone app or embed in your web application.
  • Jitsi Meet Handbook: A detailed resource to deepen your understanding and maximize your setup.

Prerequisites

We will use Traefik as a Reverse Proxy on our netcup Virtual Private Server (VPS), which we set up earlier and made accessible over the internet.

In the Traefik docker-compose.yml from my previous post, I still use traefik:v2.8.0. If you want to utilize a more recent version, check out Christian Lempa’s boilerplates and specifically his docker-compose/traefik file. Currently, this uses traefik:v3.3.1.

For this guide, I’ll assume you’re working with a VPS, have Traefik set up as a reverse proxy on this VPS, and that the VPS is accessible via the internet at your-domain.tld.

Set-Up

The process for setting up a docker compose instance is well-documented in the Self-Hosting Guide - Docker. For convenience, I’ll outline the steps here with a few additional comments to help you along the way.

First, create the file system structure needed to run Jitsi Meet on your VPS. This step should be performed as the root user on your VPS:

mkdir -p /opt/jitsi/jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}

Download and extract the latest release. DO NOT clone the git repository.

cd /opt/jitsi/
wget $(curl -s https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep 'zip' | cut -d\" -f4)

When I set this up, the file I downloaded was stable-9457-2. At the time of writing, the latest available release is stable-9909.

Unzip the package:

unzip stable-9457-2

If you’re working with the stable-9909 release, unzipping it will create a directory named jitsi-docker-jitsi-meet-4787ac5. For other versions, like the one I used (stable-9457-2), the directory created might look like jitsi-docker-jitsi-meet-2423033.

From the extracted files, you’ll only need docker-compose.yml and the file env.example. For now, let’s follow the steps outlined in the documented installation procedure.

cd jitsi-docker-jitsi-meet-2423033
cp env.example .env
bash ./gen-passwords.sh

The bash ./gen-passwords.sh step is essential for automatically filling in the XXX_AUTH_PASSWORD=... settings in the .env file.

Here’s a breakdown of the additional changes you should make to the .env file (below is a diff for additional clarity):

  • CONFIG=: Specify the directory where all configuration files will be stored.
  • PUBLIC_URL=: Enter the https://jitsi.your-domain.tld URL where your instance will be accessible online. Replace your-domain.tld with the domain name of your VPS.
  • ENABLE_AUTH=1: Enabling authentication is crucial to prevent your Jitsi instance from being publicly accessible without restrictions.
  • ENABLE_GUESTS=1: With authentication enabled, this setting allows guests to wait in a lobby until a registered user lets them in.
*** jitsi-docker-jitsi-meet-2423033/env.example	2024-04-29 18:43:49.000000000 +0200
--- .env	2025-01-10 15:09:00.035967433 +0100
***************
*** 16,22 ****
  #
  
  # Directory where all configuration will be stored
! CONFIG=~/.jitsi-meet-cfg
  
  # Exposed HTTP port
  HTTP_PORT=8000
--- 16,22 ----
  #
  
  # Directory where all configuration will be stored
! CONFIG=/opt/jitsi/jitsi-meet-cfg
  
  # Exposed HTTP port
  HTTP_PORT=8000
***************
*** 28,34 ****
  TZ=UTC
  
  # Public URL for the web service (required)
! #PUBLIC_URL=https://meet.example.com
  
  # Media IP addresses to advertise by the JVB
  # This setting deprecates DOCKER_HOST_ADDRESS, and supports a comma separated list of IPs
--- 28,34 ----
  TZ=UTC
  
  # Public URL for the web service (required)
! PUBLIC_URL=https://jitsi.your-domain.tld
  
  # Media IP addresses to advertise by the JVB
  # This setting deprecates DOCKER_HOST_ADDRESS, and supports a comma separated list of IPs
***************
*** 117,126 ****
  #
  
  # Enable authentication
! #ENABLE_AUTH=1
  
  # Enable guest access
! #ENABLE_GUESTS=1
  
  # Select authentication type: internal, jwt, ldap or matrix
  #AUTH_TYPE=internal
--- 117,126 ----
  #
  
  # Enable authentication
! ENABLE_AUTH=1
  
  # Enable guest access
! ENABLE_GUESTS=1
  
  # Select authentication type: internal, jwt, ldap or matrix
  #AUTH_TYPE=internal

Below are the changes you need to make to the docker-compose.yml file:

  • Configure the external Traefik network: At the very bottom of the file, specify traefik_net as the external network used by traefik. To find the name of this network, run the command docker network ls.
  • Update the networks: section: In both the web: and jvb: services, add traefik_net: to their respective networks: sections.
  • Set Traefik labels: In the web: and jvb: services, configure the labels: required for traefik to handle reverse proxying.
    Important Note: Refer to the Labels Block Mapping or Block Sequence Style section below for more details.
  • Expose ports for the jvb: service: Make the necessary ports: available to the VPS machine. In my setup, I had to map port 8080 to 8088 due to a local port conflict on my VPS. This step may not be needed for your configuration.
*** jitsi-docker-jitsi-meet-2423033/docker-compose.yml	2024-04-29 18:43:49.000000000 +0200
--- docker-compose.yml	2025-01-10 15:08:59.138958134 +0100
***************
*** 175,182 ****
--- 173,190 ----
              - WHITEBOARD_COLLAB_SERVER_PUBLIC_URL
          networks:
              meet.jitsi:
+             traefik_net:
          depends_on:
              - jvb
+         labels:
+           - "traefik.enable=true"
+           - "traefik.http.routers.jitsi-secure.entrypoints=web-secure"
+           - "traefik.http.routers.jitsi-secure.rule=Host(`jitsi.your-domain.tld`)"
+           - "traefik.http.routers.jitsi-secure.tls=true"
+           - "traefik.http.routers.jitsi-secure.service=jitsi"
+           - "traefik.http.services.jitsi.loadbalancer.server.port=80"
+           - "traefik.docker.network=traefik_default"
+ 
  
      # XMPP server
      prosody:
***************
*** 398,406 ****
      jvb:
          image: jitsi/jvb:${JITSI_IMAGE_VERSION:-stable-9457-2}
          restart: ${RESTART_POLICY:-unless-stopped}
!         ports:
!             - '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp'
!             - '127.0.0.1:${JVB_COLIBRI_PORT:-8080}:8080'
          volumes:
              - ${CONFIG}/jvb:/config:Z
          environment:
--- 406,413 ----
      jvb:
          image: jitsi/jvb:${JITSI_IMAGE_VERSION:-stable-9457-2}
          restart: ${RESTART_POLICY:-unless-stopped}
!         ports: # - '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp'
!             - '127.0.0.1:${JVB_COLIBRI_PORT:-8088}:8080'
          volumes:
              - ${CONFIG}/jvb:/config:Z
          environment:
***************
*** 454,460 ****
              - prosody
          networks:
              meet.jitsi:
  
  # Custom network so all services can communicate using a FQDN
  networks:
!     meet.jitsi:
--- 461,477 ----
              - prosody
          networks:
              meet.jitsi:
+             traefik_net:
+         labels:
+           - "traefik.enable=true"
+           - "traefik.udp.routers.jvb-rtr.entrypoints=video"
+           - "traefik.udp.routers.jvb-rtr.service=jvb-svc"
+           - "traefik.udp.services.jvb-svc.loadbalancer.server.port=10000"
+           - "traefik.docker.network=traefik_default"
  
  # Custom network so all services can communicate using a FQDN
  networks:
!   meet.jitsi:
!   traefik_net:
!     external:
!       name: traefik_default

After that you should be able to start your Jitsi instance on your VPS server:

docker compose up -d

Labels Block Mapping or Block Sequence Style

The Compose file reference explains that in the docker-compose.yml file, you can define the labels: section using either the mapping or sequence style.

Mapping Style: The block mapping style defines labels as key-value pairs within a map structure. It is straightforward and follows standard YAML conventions. For example:

services:
  web:
    labels:
      com.example.description: "Accounting webapp"
      com.example.department: "Finance"
      com.example.label-with-empty-value: ""

Sequence Style: The block sequence style represents each label as an individual item in a list, formatted as a key=value string. This approach is often more concise and improves readability, especially with numerous labels. For instance:

services:
  web:
    labels:
      - "com.example.description=Accounting webapp"
      - "com.example.department=Finance"
      - "com.example.label-with-empty-value"

Both styles are functionally equivalent in Docker Compose and can be used interchangeably.

Important: Within a single labels: section, you must stick to either the mapping style or the sequence style — you cannot mix the two.

In the stable-9909 release, the docker-compose.yml file introduced labels: sections that weren’t present in the older stable-9457-2 release. For example, the web: service contains the following in mapping style:

        labels:
            service: "jitsi-web"

If you decide to use the sequence style for your traefik labels (as shown in the provided diffs above), you should first convert the service: "jitsi-web" mapping to sequence style for consistency:

        labels:
            - "service=jitsi-web"

Creating Users

Authentication with ENABLE_AUTH and set AUTH_TYPE to internal, then configure the settings you can see below.

The default authentication mode (internal) uses XMPP credentials to authenticate users. To enable it you have to enable authentication with ENABLE_AUTH and set AUTH_TYPE to internal.

Internal users must be created with the prosodyctl utility in the prosody container. In order to do that, first, execute a shell in the corresponding container:

docker compose exec prosody /bin/bash

Once in the container, run the following command to create a user:

prosodyctl --config /config/prosody.cfg.lua register TheDesiredUsername meet.jitsi TheDesiredPassword

Note that the command produces no output.

Jitsi Meet configuration

Jitsi-Meet uses two configuration files for changing default settings within the web interface: config.js and interface_config.js. The files are located within the /opt/jitsi/jitsi-meet-cfg/web/ directory configured within your environment file.

These files are re-created on every container restart. To retain your custom settings, create your own configuration files named custom-config.js and custom-interface_config.js.

Refer to the Jitsi Meet user guide for a detailed explanation of configurable parameters. For example, you might want to update /opt/jitsi/jitsi-meet-cfg/web/custom-config.js to:

  • Generate avatars locally.
  • Disable Callstats integration to avoid potential data privacy issues from third-party services.

Here’s how you need to adapt /opt/jitsi/jitsi-meet-cfg/web/custom-config.js to disable third-party requests in your configuration:

config.disableThirdPartyRequests = true;

Conclusions

In this guide, you’ve learned how to set up your own Jitsi Meet video conferencing server behind traefik as a reverse proxy. By hosting your own instance, you can ensure that your video calls stay private and secure, giving you complete control over your data.

Feedback

Have you written a response to this? Let me know the URL via telegraph.

No mentions yet.