Files
2026-07-28 18:12:54 +02:00

18 KiB
Raw Permalink Blame History

Prosody XMPP Podman image

This podman image forked from SaraSmiseth's repository provides you with a configured Prosody XMPP server. Includes the prosody-migrator tool for data migrations between different database types and there is also an option to create a bridges between the XMPP server and the most popular messaging services like Telegram or Matrix, via Matterbridge. The image is based on debian:bookworm-slim. The server was tested using the iOs App Monal, Android App Conversations and the Desktop client Gajim. Multiple architectures are supported. I'd used it on a raspberry pi 4 and currently I'm using it on a x86_64 GNU/Linux machine.

While Monal and Conversations got everything set-up out-of-the-box, Gajim was used with the following extensions:

  • HttpUpload
  • Off-The-Record Encryption
  • OMEMO (requires python-axolotl to be installed)
  • Url Image preview

Table of Contents

Features

  • Secure by default
    • SSL certificate required
    • End-to-end encryption required (using OMEMO or OTR)
    • Anti-spam filter (based on the pre-installed Firewall module)
  • Data storage
    • SQLite message store
    • Configured file upload and image sharing
  • Multi-user chat (MUC)

Requirements

  • You need a SSL certificate. I recommend LetsEncrypt for that.

Image Details

Ports

The following ports are exposed:

  • 5000: proxy65 port used for file sharing
  • 5222: c2s port (client to server)
  • 5223: c2s legacy ssl port (client to server)
  • 5269: s2s port (server to server)
  • 5347: XMPP component port
  • 5280: BOSH / websocket port
  • 5281: Secure BOSH / websocket port

Directories

Data

Path: /usr/local/var/lib/prosody/.

  • used for SQLite file
  • used for HTTP uploads
  • this is exposed as podman volume

Bundled modules

Path: /usr/local/lib/prosody/modules/.

Additionally installed prosody modules

Path: /usr/local/lib/prosody/custom-modules/.

Config

Path: /usr/local/etc/prosody/.

  • containing the main config file called prosody.cfg.lua
  • containing additional config files within conf.d/

SSL certificates

Path: /usr/local/etc/prosody/certs/.

Uses automatic location to find your certs.

The http_upload module and the legacy_ssl module do not use the same search algorithm for the certificates. See service certificates.

The settings https_ssl and legacy_ssl_ssl in 05-vhost.cfg.lua configures the certificates to certs/domain.tld/fullchain.pem and certs/domain.tld/privkey.pem for legacy_ssl and to certs/DOMAIN_HTTP_UPLOAD/fullchain.pem and certs/DOMAIN_HTTP_UPLOAD/privkey.pem for http_upload where DOMAIN_HTTP_UPLOAD is an environtment variable.

Folder structure

An example certificate folder structure could look like this:

certs
├── conference.domain.tld
│   ├── fullchain.pem
│   └── privkey.pem
├── proxy.domain.tld
│   ├── fullchain.pem
│   └── privkey.pem
├── upload.domain.tld
│   ├── fullchain.pem
│   └── privkey.pem
└── domain.tld
    ├── fullchain.pem
    └── privkey.pem

Thats how Let's encrypt certbot does it out of the box.

certbot creates the structure and uses symlinks to the actual certificates. If you mount them like that prosody somehow does not find them. I copied them to a folder named certs next to my Containerfile and made sure to use the -L flag of cp. This makes cp follow symbolic links when copying from them. For example cp -L src dest.

Permissions

See official documentation for more information. Check Volumes permissions as well.

Run

First you need to build the image:

podman build -t prosody/xmpp .

Symlink Quadlet files to ~/.config/containers/systemd:

ln -s ~/podman/gitea/config/prosody-server.container ~/.config/containers/systemd/prosody-server.container
ln -s ~/podman/gitea/config/prosody-server.network ~/.config/containers/systemd/prosody-server.network

Reload systemd daemon and start service:

systemctl --user daemon-reload
systemctl --user start prosody-server
systemctl --user enable prosody-server

Inspect logs:

journalctl --user -u prosody-server -n 20 --no-pager
systemctl --user status prosody-server
podman logs --tail 20 prosody-server

Volumes permissions

The prosody user inside the container has the uid=100998 and gid=100998. Make sure, that the ./data folder and the ./certs folder have the correct permissions.

podman unshare chown -R 100998:100998 ./data/
#podman unshare chown -R 100998:100998 ./certs/

Configuration

Environment variables

Variable Description Type Default value
ALLOW_REGISTRATION Whether to allow registration of new accounts via Jabber clients optional true
DOMAIN domain required null
DOMAIN_HTTP_UPLOAD Domain which lets clients upload files over HTTP optional upload.DOMAIN
DOMAIN_MUC Domain for Multi-user chat (MUC) for allowing you to create hosted chatrooms/conferences for XMPP users optional conference.DOMAIN
DOMAIN_PROXY Domain for SOCKS5 bytestream proxy for server-proxied file transfers optional proxy.DOMAIN
DOMAIN_PUBSUB Domain for a XEP-0060 pubsub service optional pubsub.DOMAIN
AUTHENTICATION authentication optional "internal_hashed"
LDAP_BASE LDAP base directory which stores user accounts required if AUTHENTICATION is "ldap"
LDAP_SERVER Space-separated list of hostnames or IPs, optionally with port numbers (e.g. “localhost:8389”) optional "localhost"
LDAP_ROOTDN The distinguished name to auth against optional ""
LDAP_PASSWORD Password for rootdn optional ""
LDAP_FILTER Search filter, with $user and $host substituted for user- and hostname optional "(uid=$user)"
LDAP_SCOPE Search scope. other values: “base” and “onelevel” optional "subtree"
LDAP_TLS Enable TLS (StartTLS) to connect to LDAP (can be true or false). The non-standard LDAPS protocol is not supported. optional "false"
LDAP_MODE How passwords are validated. optional "bind"
LDAP_ADMIN_FILTER Search filter to match admins, works like ldap_filter optional ""
DB_DRIVER May also be "PostgreSQL" or "MySQL" or "SQLite3" (case sensitive!) optional SQLite3
DB_DATABASE The database name to use. For SQLite3 this the database filename (relative to the data storage directory). optional prosody.sqlite
DB_HOST The address of the database server optional
DB_PORT Port on which the database is listening optional
DB_USERNAME The username to authenticate to the database optional
DB_PASSWORD The password to authenticate to the database optional
HTTP_MAX_CONTENT_SIZE Max http content size in bytes optional 10485760
HTTP_FILE_SHARE_SIZE_LIMIT Max http file share size in bytes optional 10485760
HTTP_FILE_SHARE_DAILY_QUOTA Daily quota in bytes optional 10 times share size limit
E2E_POLICY_CHAT Policy for chat messages. Possible values: "none", "optional" and "required". optional "required"
E2E_POLICY_MUC Policy for MUC messages. Possible values: "none", "optional" and "required". optional "required"
E2E_POLICY_WHITELIST Make this module ignore messages sent to and from this JIDs or MUCs. optional ""
LOG_LEVEL Min log level. Change to debug for more information optional info
C2S_REQUIRE_ENCRYPTION Whether to force all client-to-server connections to be encrypted or not optional true
S2S_REQUIRE_ENCRYPTION Whether to force all server-to-server connections to be encrypted or not optional true
S2S_SECURE_AUTH Require encryption and certificate authentication optional true
SERVER_CONTACT_INFO_ABUSE A list of strings. Each string should be an URI. See here. optional "xmpp:abuse@DOMAIN"
SERVER_CONTACT_INFO_ADMIN A list of strings. Each string should be an URI. See here. optional "xmpp:admin@DOMAIN"
SERVER_CONTACT_INFO_FEEDBACK A list of strings. Each string should be an URI. See here. optional "xmpp:feedback@DOMAIN"
SERVER_CONTACT_INFO_SALES A list of strings. Each string should be an URI. See here. optional "xmpp:sales@DOMAIN"
SERVER_CONTACT_INFO_SECURITY A list of strings. Each string should be an URI. See here. optional "xmpp:security@DOMAIN"
SERVER_CONTACT_INFO_SUPPORT A list of strings. Each string should be an URI. See here. optional "xmpp:support@DOMAIN"
PROSODY_ADMINS Specify who is an administrator. List of adresses. Eg. "me@example.com", "admin@example.net" optional ""
ENABLE_FIREWALL Enable Firewall module optional false
SPAM_BLOCKLIST Blacklist to use with Firewall module. Eg. "custom-blocklist.pfw" optional

DNS

You need these DNS record pointing to your server:

  • domain.tld
  • conference.domain.tld
  • proxy.domain.tld
  • pubsub.domain.tld
  • upload.domain.tld
  • A SRV record for _xmpps-client._tcp.domain.tld for port 5223.

where domain.tld is the environment variable DOMAIN.

Extend

There is a helper script that eases installing additional prosody modules: podman-prosody-module-install

It downloads the current prosody-modules repository. The specified modules are copied and its name is added to the modules_enabled variable within conf.d/01-modules.cfg.lua.

There is also podman-prosody-module-pre-install.bash which downloads the specified modules but does not add them to the modules_enabled variable within conf.d/01-modules.cfg.lua. In fact, this script is in charge of pre-installing the Firewall module.

If you need additional configuration just overwrite the respective cfg.lua file or add new ones.

Upgrade

When migrating from prosody 0.10, you need to update the database once:

podman exec server bash
prosodyctl mod_storage_sql upgrade

Matterbridge

All needed to enable bridges between 3rd party messengers using Matterbridge is configured in matterbridge.container Quadlet. So add toml config file in matterbridge directory spedifying protocols and gateways. Check the documentation for more information.

Migration tool

You need to set up migrator.cfg.lua config file before build the image. Once your server is working you could use the migration tool in this way:

podman exec -it prosody-server prosody-migrator -h

Test your server

You can test your server with these websites: