Deploying Jitsi Meet with Docker and Nginx Proxy Manager
Learn how to self-host Jitsi Meet with Docker Compose and Nginx Proxy Manager. This detailed guide covers directory setup, HTTPS management, UDP port configuration, and admin account creation for a fully secure video conferencing platform.
Introduction: Why Self-Host Jitsi Meet?
Jitsi Meet is an open-source WebRTC-powered video conferencing platform that rivals Zoom or Google Meet — but with one crucial difference: you control it completely.
Unlike proprietary services that store your video data in the cloud, Jitsi Meet allows you to host your own meetings, on your own hardware, under your own rules. For developers, educators, and privacy-focused teams, that’s a game changer.
In this tutorial, we’ll walk through deploying Jitsi Meet with Docker Compose, fronted by Nginx Proxy Manager (NPM) for HTTPS and reverse proxy management. You’ll also learn to organize your project folders, expose required ports, and create admin users for secure access control.
Step 1: Folder Structure — The Foundation of a Clean Setup
Before writing your docker-compose.yaml, it’s best practice to create a clear folder hierarchy. Each service — Prosody, Jicofo, JVB, and the Web interface — will store its configuration in mounted volumes for persistence.
Here’s the recommended layout:
jitsi-meet-docker/
├── docker-compose.yaml # Main Docker Compose stack
│
├── prosody/ # Prosody (XMPP signaling server)
├── jicofo/ # Jicofo (conference orchestrator)
├── jvb/ # Jitsi Video Bridge (media router)
├── web/ # Web frontend (Nginx + Jitsi Meet UI)
└── transcripts/ # Meeting transcripts and captions
Step 2: Create the Folder Structure in One Command
You can create all necessary directories with a single command from the root of your project:
mkdir -p prosody jicofo jvb web transcripts
Once this command runs, your file structure will be ready to mount into the containers defined in docker-compose.yaml.
Step 3: Complete docker-compose.yaml Configuration
Now, let’s build the main Docker Compose file that brings all the components together.
This configuration assumes you are using Nginx Proxy Manager (NPM) for SSL and reverse proxy management.
If you’re not using NPM, a commented section shows how to enable internal HTTPS via Let’s Encrypt inside the jitsi-web container.
🧩 docker-compose.yaml
services:
# 1) XMPP Server — Handles signaling, sessions, and authentication
jitsi-prosody:
image: jitsi/prosody:stable
container_name: jitsi-prosody
restart: unless-stopped
networks: [jitsi-meet, edge]
environment:
- TZ=Asia/Seoul # Change for production
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_GUEST_DOMAIN=guest.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
- ENABLE_AUTH=1 # Enable authentication
- AUTH_TYPE=internal
- ENABLE_GUESTS=1 # Allow guests to join meetings
- GLOBAL_MODULES=smacks
- GLOBAL_CONFIG=restrict_room_creation = true;
# 🔑 These credentials must match across prosody, jicofo, and jvb
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD=focuspass! # Change for production
- JICOFO_COMPONENT_SECRET=focuscomponent! # Change for production
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD=jvbpass! # Change for production
volumes:
- ./prosody:/config
# 2) Conference Orchestrator — Jicofo manages meetings and participants
jitsi-jicofo:
image: jitsi/jicofo:stable
container_name: jitsi-jicofo
restart: unless-stopped
depends_on: [jitsi-prosody]
networks: [jitsi-meet, edge]
environment:
- TZ=Asia/Seoul # Change for production
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_SERVER=jitsi-prosody
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD=focuspass! # Must match prosody
- JICOFO_COMPONENT_SECRET=focuscomponent! # Must match prosody
- ENABLE_AUTH=1
volumes:
- ./jicofo:/config
# 3) Jitsi Video Bridge — Handles audio/video media streams
jitsi-jvb:
image: jitsi/jvb:stable
container_name: jitsi-jvb
restart: unless-stopped
depends_on: [jitsi-prosody]
networks: [jitsi-meet, edge]
ports:
- "10000:10000/udp" # ✅ WebRTC media port (must be open externally)
# - "4443:4443/tcp" # Optional TCP fallback port
environment:
- TZ=Asia/Seoul # Change for production
- XMPP_SERVER=jitsi-prosody
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD=jvbpass! # Change for production
# Public IP or domain for NAT environments
- JVB_ADVERTISE_ADDRESS=jitsi.example.com
volumes:
- ./jvb:/config
# 4) Web Frontend — Serves Jitsi Meet UI via internal Nginx
jitsi-web:
image: jitsi/web:stable
container_name: jitsi-web
restart: unless-stopped
depends_on: [jitsi-prosody, jitsi-jicofo, jitsi-jvb]
networks: [jitsi-meet, edge]
# ⚠ No exposed ports here — Nginx Proxy Manager will handle public access
environment:
- TZ=Asia/Seoul # Change for production
- PUBLIC_URL=https://jitsi.example.com
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_BOSH_URL_BASE=http://jitsi-prosody:5280
- ENABLE_GUESTS=1
- ENABLE_AUTH=1
# ▼▼▼▼▼ Use this only if NOT using NPM (direct HTTPS inside container) ▼▼▼▼▼
# - ENABLE_LETSENCRYPT=1
# - LETSENCRYPT_DOMAIN=jitsi.example.com
# - LETSENCRYPT_EMAIL=admin@example.com
# - HTTPS_PORT=443
# ports:
# - "80:80"
# - "443:443"
# ▲▲▲▲▲ Comment out the above if using NPM for certificate management ▲▲▲▲▲
volumes:
- ./web:/config
- ./transcripts:/usr/share/jitsi-meet/transcripts
networks:
jitsi-meet:
edge:
external: true
Step 4: Understanding the Configuration
Network Architecture
- Two Docker networks are used:
- jitsi-meet (internal communication)
- edge (shared with Nginx Proxy Manager)
This allows NPM to reach the jitsi-web container through its internal hostname (jitsi-web:80).
Port Exposure
- The only public port required is UDP 10000 (media traffic).
- Optionally, TCP 4443 can serve as a fallback.
- No ports from
jitsi-webare exposed because NPM handles external traffic.
Authentication
- Internal authentication is enabled (
ENABLE_AUTH=1,AUTH_TYPE=internal). - Only authenticated users can create rooms.
- Guests can join existing rooms if
ENABLE_GUESTS=1.
Persistent Volumes
- All configuration and user data persist across restarts.
You can safely update images with:
docker compose pull && docker compose up -d
Step 5: Nginx Proxy Manager (NPM) Configuration
NPM acts as your HTTPS reverse proxy and SSL manager. It simplifies domain routing and certificate renewals with Let’s Encrypt.
Proxy Host Setup
- Add a New Proxy Host
- Domain Names:
jitsi.example.com - Scheme:
http - Forward Hostname / IP:
jitsi-web - Forward Port:
80
- Domain Names:
- Enable Options
- ✅ Block Common Exploits
- ✅ WebSocket Support
- SSL Tab
- Request a new SSL certificate via Let’s Encrypt
- Enable Force SSL, HTTP/2, and HSTS
Advanced Nginx Configuration (Recommended)
To ensure stable WebSocket connections and prevent timeouts, add the following in the Advanced tab:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
client_max_body_size 0;
proxy_set_header X-Forwarded-Proto $scheme;
Step 6: Start the Stack
Once your docker-compose.yaml and NPM configuration are ready:
docker compose up -d
To verify the services:
docker ps
You should see containers named:
jitsi-prosody
jitsi-jicofo
jitsi-jvb
jitsi-web
Access the site via your domain (e.g., https://jitsi.example.com).
If everything is configured correctly, you’ll see the Jitsi Meet landing page.
Step 7: The UDP 10000 Requirement
This port is non-negotiable.
Jitsi’s media bridge (jvb) uses UDP 10000 for WebRTC communication. If it’s closed or filtered, participants won’t see or hear each other.
Open the port at all levels:
# Example (UFW firewall)
sudo ufw allow 10000/udp
sudo ufw reload
In cloud environments (AWS, GCP, etc.), also open it in your Security Group or Firewall Rules.
Step 8: Create an Admin Account
To create rooms and manage access, register an admin user inside the Prosody container:
docker exec -it jitsi-prosody /usr/bin/prosodyctl \
--config /config/prosody.cfg.lua register admin meet.jitsi 'StrongPassword123!'
This creates an account admin@meet.jitsi.
To make this account a global administrator, edit:
./prosody/prosody.cfg.lua
Add:
admins = { "admin@meet.jitsi" }
Restart related services:
docker compose restart jitsi-prosody jitsi-jicofo jitsi-web
Now, when creating a new room, Jitsi will prompt for authentication.
Guests can join afterward if ENABLE_GUESTS=1.
Step 9: Verifying the Installation
Visit your domain:
https://jitsi.example.com
You should see the Jitsi interface load.
If the meeting connects but video/audio fails:
- Verify UDP 10000 is open.
- Confirm NPM has WebSocket Support enabled.
- Use your browser’s developer console to check for failed
BOSHor WebSocket requests.
Check logs:
docker logs jitsi-jvb
docker logs jitsi-prosody
Step 10: Performance & Quality Optimization
To make your deployment production-ready, tweak these parameters:
- Enable a TURN Server:
For restrictive networks, integratecoturnfor relay support. - Limit Resolution:
Reduce video quality to 720p or 360p to improve bandwidth efficiency. - Bandwidth Control:
Use Jitsi’s config.js to limit max upload/download bitrate. - Enable TCP Fallback:
Open port 4443/tcp as a backup route when UDP is blocked.
Step 11: Security Best Practices
Security should not be an afterthought.
- Rotate secrets regularly:
Change passwords forJICOFO_AUTH_PASSWORD,JVB_AUTH_PASSWORD, andJICOFO_COMPONENT_SECRET. - Restrict port exposure:
Only UDP 10000 should be public. - Backup volumes:
Archive theprosody,jicofo,jvb, andwebdirectories periodically.
Monitor logs:
docker logs -f jitsi-prosody
docker logs -f jitsi-jvb
Apply automatic updates:
docker compose pull && docker compose up -d
Step 12: Troubleshooting Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Meeting connects but no video/audio | UDP 10000 blocked | Open port or configure TURN |
| Only one user visible | NAT/Firewall interference | Set JVB_ADVERTISE_ADDRESS correctly |
| Screen share drops | Bandwidth limits or WebSocket timeout | Increase read timeout in NPM |
| Can’t create rooms | No admin account | Register admin via prosodyctl |
| SSL errors | NPM misconfiguration | Reissue Let’s Encrypt certificate |
| Guests can’t join | ENABLE_GUESTS disabled | Set ENABLE_GUESTS=1 and restart |
Conclusion: Your Open-Source Zoom Alternative
By following this guide, you now have a fully functional, self-hosted Jitsi Meet server powered by Docker and Nginx Proxy Manager.
You own the infrastructure, data, and user control — with modern WebRTC performance and enterprise-grade security.
From here, you can extend your deployment with:
- JWT or SSO authentication (Keycloak/OIDC)
- TURN/STUN servers for better connectivity
- Monitoring via Prometheus or Grafana
- Integration with your organization’s internal systems
In short:
You’ve built your own privacy-respecting, scalable alternative to Zoom — right on your own terms.
“Owning your communication infrastructure isn’t just a technical choice.
It’s an act of digital independence.”
External Reference:
🔗 Official Jitsi Docker Repository