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
6) Create Prosody Users for Authentication (Recommended: Use Default meet.jitsi)
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 ismeet.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.ymltells 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:
/root/Jitsi-meet/keys/meet.shonizcloud.ir.pem/root/Jitsi-meet/keys/meet.shonizcloud.ir.key
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:
websecurerouter serves HTTPS.Separate
webrouter redirects HTTP → HTTPS.loadbalancer.server.port=80tells 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-meetfrom the latest official release ZIPOrganized 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.jitsiConfigured 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



