Skip to main content

Command Palette

Search for a command to run...

Deploying Jitsi Meet Behind Traefik with Docker Compose (Custom SSL PEM/KEY + Host Authentication)

Updated
5 min read
Deploying Jitsi Meet Behind Traefik with Docker Compose (Custom SSL PEM/KEY + Host Authentication)

This guide walks through how we deployed Jitsi Meet using Docker Compose, placed it behind Traefik, terminated TLS using our own certificate (.pem + .key) via Traefik’s File Provider, and enabled Host Authentication using internal auth (Prosody users).


1) Target Setup and Architecture

Goals:

  • Traefik is already listening on 443 (used by other services).

  • Jitsi must run behind Traefik and not take over port 443 directly.

  • TLS must be served using a custom certificate (not Let’s Encrypt).

  • Enable Host Authentication (internal auth), so only authenticated users can host/start meetings (and optionally allow guests to join).

Traffic flow:

Internet (https://meet.shonizcloud.ir)
          |
          v
     Traefik :443    (TLS termination using custom cert)
          |
          v
    Jitsi Web :80     (inside Docker network)
          |
          +--> Prosody / Jicofo / JVB ...

2) Get the Official Jitsi Docker Compose Files (Release ZIP)

We keep all Docker-based apps under a single parent folder so backup is easy (zip the parent folder).

Create the folder structure:

mkdir -p docker/jitsi
cd docker/jitsi

Download the latest official docker-jitsi-meet release ZIP:

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

Install unzip:

Ubuntu / Debian

sudo apt install unzip

RedHat / CentOS / Fedora

sudo dnf install unzip

Unzip the downloaded release (replace <version> with the actual filename you downloaded):

unzip ./stable-<version>

Rename the extracted folder to something clean:

mv jitsi-docker-jitsi-meet-<hash-or-version> jitsi-meet
cd jitsi-meet

3) Copy Environment and Compose Files

Create your working .env from the example:

cp env.example .env

Copy the compose file to the newer naming convention (compose.yaml) and back up the original:

cp docker-compose.yml compose.yaml
mv docker-compose.yml docker-compose.yml.bak

4) Edit .env (Config Path, Domain, IPs, and Internal Authentication)

Open .env:

nano .env

4.1 Put the config volume inside the project directory

This keeps all persistent configuration under the project folder (easy backups):

CONFIG=./.jitsi-meet-cfg

4.2 Ports

If the default ports (8000, 8443) are free, you can keep them. If other apps use them, change them here and remember the values for your reverse proxy design.

4.3 Timezone

Set the correct timezone for your server, for example:

TZ=Europe/Berlin

4.4 PUBLIC_URL

This is very important. It must match the external URL users will browse to:

PUBLIC_URL=https://meet.shonizcloud.ir

4.5 JVB_ADVERTISE_IPS

For NAT or multi-path access scenarios, you can set:

JVB_ADVERTISE_IPS=192.168.21.14,209.41.5.158

Very important note:
The public IP you put here must be the exact public IP address that external users see and connect to (i.e., the Internet/WAN-facing IP).
If your server is behind NAT/firewall, this should not be your internal/private IP—use the public (NATed) IP that inbound traffic actually reaches. Optionally, you can include your private LAN IP first, then the public IP.

4.6 Enable Internal Authentication (Host Authentication)

In the Authentication section, enable:

ENABLE_AUTH=1
ENABLE_GUESTS=1
AUTH_TYPE=internal
  • With ENABLE_GUESTS=1, unauthenticated users can join once a host starts the meeting.

  • If you don’t want guests at all, do not enable ENABLE_GUESTS.

4.7 Generate Strong Internal Passwords

Exit the editor and run:

./gen-passwords.sh

Re-open .env and verify the Security section got populated with strong random passwords.

4.8 Enable Restart Policy

Set:

RESTART_POLICY=unless-stopped

5) Pull Images and Start Jitsi

Pull all images:

docker compose pull

Start the stack and follow logs:

docker compose up -d && docker compose logs -f

If you try to create a Prosody user and see:

The given hostname does not exist in the config

it means the domain you used is not a configured VirtualHost in Prosody (or you are pointing prosodyctl at the wrong config).

To see the configured VirtualHosts:

docker compose exec prosody sh -lc "grep -RIn 'VirtualHost' /config | head -n 50"

In docker-jitsi-meet, VirtualHosts are commonly defined here:

  • /config/conf.d/jitsi-meet.cfg.lua

Recommendation: To avoid unnecessary complexity with XMPP domain changes, create users using the default internal XMPP domain meet.jitsi, even if your external web domain is meet.shonizcloud.ir.

Create a user like this:

docker compose exec prosody prosodyctl --config /config/conf.d/jitsi-meet.cfg.lua \
  register mahdi meet.jitsi mahdi123

7) Put Jitsi Behind Traefik

In this design, Traefik handles HTTPS and forwards traffic internally to Jitsi over HTTP (port 80 inside the Docker network).


8) Full Traefik Configuration for Custom SSL (PEM/KEY) Using File Provider

We disabled Let’s Encrypt for this site and used our own certificate.

8.1 Traefik Docker Compose (full config used)

services:
  traefik:
    image: "docker-mirror.kubarcloud.com/traefik"
    restart: always
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.file.filename=/dynamic/tls.yml"
      - "--providers.file.watch=true"
      - "--entrypoints.web.address=:80"
     # - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
     # - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
    #  - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
    #  - "--certificatesresolvers.mytlschallenge.acme.email=mahdishadi99@gmail.com"
    #  - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    networks:
      - radar
    volumes:
      - traefik_data:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /root/Dashboard-Internet/dynamic:/dynamic:ro
      - /root/Jitsi-meet/keys:/etc/traefik/certs:ro

Notes:

  • --providers.file.filename=/dynamic/tls.yml tells Traefik to load TLS certs from this file.

  • We mount the cert directory so Traefik can read the PEM/KEY inside the container:

    • /root/Jitsi-meet/keys/etc/traefik/certs

8.2 TLS config file (tls.yml)

Path on the host:

/root/Dashboard-Internet/dynamic/tls.yml

Content:

tls:
  certificates:
    - certFile: /etc/traefik/certs/meet.shonizcloud.ir.pem
      keyFile: /etc/traefik/certs/meet.shonizcloud.ir.key

This means the following files must exist on the host:


9) Jitsi Labels for Traefik (HTTPS + Redirect + Security Headers)

Apply these labels to the Jitsi web service to route meet.shonizcloud.ir through Traefik:

labels:
  - traefik.enable=true
  - traefik.http.routers.meet.rule=Host(`meet.shonizcloud.ir`)
  - traefik.http.routers.meet.entrypoints=websecure
  - traefik.http.routers.meet.tls=true
  - traefik.http.routers.meet.middlewares=meet-headers@docker
  - traefik.http.services.meet-svc.loadbalancer.server.port=80

  - traefik.http.middlewares.meet-headers.headers.STSSeconds=315360000
  - traefik.http.middlewares.meet-headers.headers.forceSTSHeader=true
  - traefik.http.middlewares.meet-headers.headers.STSIncludeSubdomains=true
  - traefik.http.middlewares.meet-headers.headers.STSPreload=true
  - traefik.http.middlewares.meet-headers.headers.browserXSSFilter=true
  - traefik.http.middlewares.meet-headers.headers.contentTypeNosniff=true

  - traefik.http.routers.meet-http.rule=Host(`meet.shonizcloud.ir`)
  - traefik.http.routers.meet-http.entrypoints=web
  - traefik.http.routers.meet-http.middlewares=meet-redirect@docker
  - traefik.http.middlewares.meet-redirect.redirectscheme.scheme=https

Key points:

  • websecure router serves HTTPS.

  • Separate web router redirects HTTP → HTTPS.

  • loadbalancer.server.port=80 tells Traefik to forward to the Jitsi web container on port 80.


10) Verify the Certificate Served by Traefik

To confirm Traefik is presenting your custom certificate:

echo | openssl s_client -connect meet.shonizcloud.ir:443 -servername meet.shonizcloud.ir 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

11) Summary

What we achieved:

  • Downloaded and deployed docker-jitsi-meet from the latest official release ZIP

  • Organized the project for easy backup (docker/jitsi/jitsi-meet)

  • Configured .env (including internal auth + generated strong passwords)

  • Started the Jitsi stack using Docker Compose

  • Recommended creating Prosody users on the default XMPP domain meet.jitsi

  • Configured Traefik to terminate TLS using a custom PEM/KEY certificate via File Provider

  • Added complete Traefik labels to route Jitsi over HTTPS and redirect HTTP to HTTPS