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

- Linux & Network Lover 💻
- DevOps Student
- Chess Player ♟️
Search for a command to run...

No comments yet. Be the first to comment.
At some point, logs alone stop being enough. You start seeing short spikes in CPU, random latency jumps, or brief outages that don’t leave a clear trace in the logs. That’s where real monitoring pays off: numbers, charts, history, and alerts. This gu...

A practical, battle-tested guide to wiring n8n, Traefik, Docker networks, and Let’s Encrypt into a production-ready stack.

If you want to set up secure and scalable data storage in Kubernetes, this article is exactly what you need! We’ll show you how to install Longhorn an

When your monitoring environment grows, it starts screaming for an upgrade. More hosts, items, and triggers appear, the UI slows down, caches run out

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).
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 ...
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:
sudo apt install unzip
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
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
.env (Config Path, Domain, IPs, and Internal Authentication)Open .env:
nano .env
This keeps all persistent configuration under the project folder (easy backups):
CONFIG=./.jitsi-meet-cfg
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.
Set the correct timezone for your server, for example:
TZ=Europe/Berlin
This is very important. It must match the external URL users will browse to:
PUBLIC_URL=https://meet.shonizcloud.ir
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.
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.
Exit the editor and run:
./gen-passwords.sh
Re-open .env and verify the Security section got populated with strong random passwords.
Set:
RESTART_POLICY=unless-stopped
Pull all images:
docker compose pull
Start the stack and follow logs:
docker compose up -d && docker compose logs -f
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.luaRecommendation: 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
In this design, Traefik handles HTTPS and forwards traffic internally to Jitsi over HTTP (port 80 inside the Docker network).
We disabled Let’s Encrypt for this site and used our own certificate.
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/certstls.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
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.
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
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