# Prosody XMPP Server Dockerized Prosody XMPP server with MySQL backend. ## Prerequisites - Docker and Docker Compose - External MySQL network: `mysqlgit_mysql_network` - Environment variables configuration (see Configuration section) ## Configuration This setup uses environment variables for configuration. Create a `.env` file in the project root with: ```env # XMPP Configuration XMPP_DOMAIN=xmpp.guschin.info XMPP_USER=mish XMPP_PASSWORD=your_password # MySQL Configuration MYSQL_HOST=mysql MYSQL_USER=prosody MYSQL_PASSWORD=your_mysql_password MYSQL_DATABASE=prosody ``` ## Starting the Server Run from the project directory: ```bash docker-compose up -d ``` This will: - Build and start the Prosody container - Connect to external MySQL network (`mysqlgit_mysql_network`) - Wait for MySQL to be ready - Generate self-signed SSL/TLS certificates (if not present) - Configure Prosody with MySQL backend ## Access Points | Service | Port | Description | |---------|------|-------------| | C2S (Client to Server) | 5222 | XMPP client connections | | S2S (Server to Server) | 5269 | XMPP federation | | Component Protocol | 5347 | External components | | HTTP/BOSH/WebSocket | 5280 | Web-based connections | | HTTPS | 5281 | Secure web connections | | STUN/TURN | 3478 (UDP+TCP) | NAT traversal for audio/video calls | | STUN/TURN over TLS | 5349 (UDP+TCP) | Encrypted TURN (`turns:`) | | TURN relay range | 49160-49200 (UDP) | Media relay ports | ## Features - **Virtual Host**: `xmpp.guschin.info` - **Multi-User Chat (MUC)**: `muc.xmpp.guschin.info` - **Storage**: MySQL backend with SQL storage - **Admin User**: `admin@guschin.info` - **Health Checks**: Automatic container health monitoring - **Memory Limit**: 200M - **Auto-restart**: Container restarts unless stopped ## Enabled Modules Core modules: roster, saslauth, tls, dialback, disco, carbons, pep, private, blocking, vcard, version, uptime, time, ping, register, last_activity, admin_adhoc, muc_mam ## Data Persistence Volumes are mounted to persist data: - `./data/prosody` → `/var/lib/prosody` - `./logs/prosody` → `/var/log/prosody` - `./data/prosody/configuration` → `/etc/prosody/conf.d` ## Connecting Clients Use any XMPP client with: - **Server**: xmpp.guschin.info - **Port**: 5222 - **Username**: your configured user - **Domain**: xmpp.guschin.info ## Audio/Video Calls (Jingle) Voice and video calls use Jingle (XEP-0166/0167/0176). Prosody only carries the signalling; media flows directly between clients, falling back to a TURN relay when NAT prevents a direct path. Two pieces make this work: - **coturn** (`coturn` container) — the STUN/TURN server. Runs with host networking because a relay needs its whole UDP port range reachable. - **`mod_turn_external`** in Prosody — advertises coturn to clients over XEP-0215 and issues time-limited TURN credentials. ### Configuration `TURN_SECRET` in `.env` must match `static-auth-secret` in [coturn/turnserver.conf](coturn/turnserver.conf). `.env` is gitignored, so these values are set per-deployment and not committed: ```env TURN_HOST=guschin.info TURN_SECRET= ``` `TURN_HOST` is `guschin.info`, not `turn.guschin.info`: `turns:` (TLS) clients validate the hostname against the certificate, and only `guschin.info` is in the current cert's SANs. To use `turn.guschin.info` instead, reissue the certificate with that name added and update both `TURN_HOST` and the `external-ip`/`cert` settings accordingly. `turnserver.conf` pins the server's public address in three places: `listening-ip`, `relay-ip`, and `external-ip`. **Update all three if the server IP changes**, otherwise coturn advertises relay candidates that peers cannot reach. Pinning them also stops coturn from auto-discovering the Docker bridge interfaces and relaying on unreachable private addresses. ### Verifying Check that Prosody advertises the service and coturn answers: ```bash docker logs coturn # should show listeners on 3478/5349 docker exec prosody-xmpp prosodyctl check config ``` From a client, place a call between two accounts. Clients that support Jingle audio/video include Conversations (Android), Dino, Gajim, and Monal (iOS). Note that Jingle A/V is client-to-client — both parties need a client that supports it. ### Certificate renewal `coturn/entrypoint.sh` copies the certificate at container start, because coturn runs as `nobody` and cannot read Let's Encrypt's root-only `privkey.pem` directly. A renewal therefore needs a container restart, which the certbot deploy hook on the server handles (outside this repo): ``` /etc/letsencrypt/renewal-hooks/deploy/restart-prosody.sh ``` It restarts both `prosody-xmpp` and `coturn` when the `guschin.info` or `xmpp.guschin.info` lineage is renewed. Without the coturn restart, `turns:` on 5349 would keep serving the old certificate until the next restart. ### Firewall Ports 3478 and 5349 (both UDP and TCP) plus the UDP relay range 49160-49200 must be reachable from the internet. The host has no local firewall (`iptables` INPUT policy is ACCEPT), but any provider-level firewall needs these opened. ## Notes - TLS encryption is not required by default (configurable in [prosody.cfg.lua](data/prosody/configuration/prosody.cfg.lua)) - Self-signed certificates are auto-generated on first run - The container depends on an external MySQL network named `mysqlgit_mysql_network`