mirror of
https://gitverse.ru/kpa39l/xmpp-server-prosody.git
synced 2026-09-29 09:45:02 +00:00
7.4 KiB
7.4 KiB
name, title, description
| name | title | description |
|---|---|---|
| xmpp-server-prosody | XMPP Server Deployment (Prosody) | Deploy federated XMPP (Prosody in Docker) behind a proxy. |
XMPP Server Deployment (Prosody)
Deploy a self-hosted federated XMPP messenger (family/team) with Prosody in Docker, exposed through a public VPS reverse proxy. Produced from the ICQ project (chat.nixg.ru) on the bigbox/vps02 infrastructure.
When to use
- User wants a self-hosted messenger (family, team) with iOS/Android clients and federation (talking to the whole XMPP network).
- Choosing XMPP: it is the federated standard (like email). Do not attempt to "clone Telegram/MTProto" — it is closed, centralised, non-federated. XMPP is the right answer when federation matters.
- Snikket vs Prosody: Snikket server is easier but assumes direct 443 access and is finicky behind a reverse proxy. Prosody is the right choice when going through a Caddy/reverse-proxy + WireGuard setup.
Architecture (this environment)
[Clients: Monal/Snikket/Conversations iOS+Android]
│ c2s 5222 (TLS)
▼
[bigbox] Prosody Docker (/opt/icq) — no public IP
│ s2s 5269 (federation) · http 5280 (websocket/BOSH)
▼
[vps02] Caddy (host-network, reverse_proxy 10.8.0.2:5280) + iptables DNAT 5222/5269 → 10.8.0.2
▲
└─ WireGuard wg0 (10.8.0.x)
DNS records (critical for federation)
A chat.nixg.ru → public IP of vps02
SRV _xmpp-client._tcp.chat.nixg.ru → chat.nixg.ru:5222 (priority 0, weight 5)
SRV _xmpp-server._tcp.chat.nixg.ru → chat.nixg.ru:5269 (priority 0, weight 5)
A conference.chat.nixg.ru → public IP (MUC component host)
- Without SRV records, other servers cannot find you → federation silently fails. For a standard XMPP domain hosted at a subdomain, prosodyctl suggests using SRV to redirect; that is fine.
- A-record alone works for clients hardcoded to the domain, but SRV is what the federated network uses.
docker-compose.yml
services:
prosody:
image: prosody/prosody:latest
container_name: icq-prosody
restart: unless-stopped
hostname: chat.nixg.ru
volumes:
- ./data:/var/lib/prosody
- ./config:/etc/prosody
- ./certs:/etc/prosody/certs
- ./modules:/etc/prosody/modules
- ./logs:/var/log/prosody
ports:
- "5222:5222" # c2s clients
- "5269:5269" # s2s federation
- "5280:5280" # BOSH/websocket (behind Caddy)
# - "5281:5281" # https — SKIP unless you have a cert wired
Remove the obsolete version: attribute (Compose v2 warns).
prosody.cfg.lua pitfall checklist
modules_enabledmust NOT containmuc— MUC is a Component (Component "conference.chat.nixg.ru" "muc"). Loadingmucas a module errors out.muc_mamloads only on a MUC component, not on the host.logblock must be in the GLOBAL section, above any VirtualHost/Component, orprosodyctl check configcomplains.- Community modules NOT in the stock image (prosody/prosody:latest):
http_upload,smacks,s2s_bidi,xmpp_component. They come from the prosody-modules community repo — either install them or leave them out. HTTP Upload (XEP-0363, needed for bot file delivery) requires installingmod_http_uploadmanually. prosodyctl register <user> <domain> <pass>works inside the container; do NOT use-it(fails "cannot attach stdin"). Data dir is URL-encoded:/var/lib/prosody/chat%2enixg%2eru/accounts/.- https 5281 bind error ("No certificate present") is benign — the stock image tries to bind it by default. Disable by not mapping the port; it does not break c2s/s2s/http.
- Prosody 13.0 != 0.11 for external components:
component_portswas REMOVED (listener is hardcoded 5347); a module-lessComponent "jid"block is required to raise the listener;component_interfacesmust be in the GLOBAL section;log = {...}may only appear ONCE in the config. mod_privilege (XEP-0356) needs the TIP version (promise API) and must be enabled on BOTH the component block and the VirtualHost. Full recipe + gotchas:references/prosody-13-parallel-stand.md. - Validate:
docker exec icq-prosody prosodyctl check config→ "All checks passed". allow_registration = trueenables in-band registration — flip off after family accounts are created.
Exposing through vps02 (Caddy + WireGuard + iptables)
Caddy on vps02 runs network_mode: host with a plain Caddyfile. Add:
chat.nixg.ru {
reverse_proxy 10.8.0.2:5280 {
header_up Host {host}
header_up X-Forwarded-Proto https
}
}
Caddy auto-issues Let's Encrypt TLS. Reload: docker exec caddy caddy reload --config /etc/caddy/Caddyfile.
For non-HTTP ports (5222 c2s, 5269 s2s) Caddy cannot proxy them — use iptables DNAT on vps02. Existing pattern (mirrors the 8443 rule already present):
sudo iptables -t nat -A PREROUTING -p tcp --dport 5222 -j DNAT --to-destination 10.8.0.2:5222
sudo iptables -t nat -A PREROUTING -p tcp --dport 5269 -j DNAT --to-destination 10.8.0.2:5269
sudo iptables -I INPUT -p tcp --dport 5222 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 5269 -j ACCEPT
sudo iptables -I FORWARD 1 -d 10.8.0.2 -p tcp --dport 5222 -j ACCEPT
sudo iptables -I FORWARD 1 -d 10.8.0.2 -p tcp --dport 5269 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o wg0 -p tcp --dport 5222 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -o wg0 -p tcp --dport 5269 -j MASQUERADE
# persist:
sudo sh -c "iptables-save > /etc/iptables/rules.v4"
- FORWARD policy is DROP by default on vps02 — you MUST add FORWARD ACCEPT rules (the 8443 precedent has them). DNAT alone is not enough.
- Save via
sudo sh -c "iptables-save > /etc/iptables/rules.v4"— plainsudo iptables-save > filefails (redirect runs as your user, not root).netfilter-persistentservice is enabled.
Testing the external path — hairpin NAT trap
- Do NOT test a host's public ports from the same host (or from a host that routes back into it).
ip route get <pub-ip>shows<local>→ traffic never leaves the box, DNAT never fires.Connection refusedfrom such tests is a FALSE NEGATIVE. - Validate the path instead:
- From vps02 →
10.8.0.2:5222over WG (real path, with tcpdump on bigbox wg0 shows the TCP handshake). - From an EXTERNAL host (phone on mobile data, another VPS):
nc -vz chat.nixg.ru 5222.
- From vps02 →
- If both internal paths work but an external probe fails, suspect the provider's cloud firewall panel (Timeweb etc.) — ports 5222/5269 are commonly closed there by default. Ask the user to open them in the provider panel.
- 443 via Caddy IS externally testable from anywhere; a working 443 + working WG path to 5222 usually means only the provider firewall is in the way.
Bots (slixmpp) and file delivery
- An XMPP bot is just a second user account (JID) — no separate Bot API. Python
slixmppis the live, mature library. - To send files (e.g. a book-download bot), the server needs HTTP Upload (XEP-0363) — community module
mod_http_upload(not in stock image). The client sends the file to the upload component; the recipient gets a link. - Bot pattern: user messages
book@chat.nixg.ru→ bot queries an OPDS catalog (Flibusta et al.) → downloads epub/fb2 → uploads via HTTP Upload → sends link.
References
references/vps02-bigbox-wg-access.md— exact vps02/bigbox network topology, Caddyfile, iptables persistence and hairpin-test evidence.