Self-hosting UnifiedPush with Conversations as Distributor

You might have heard that XMPP in general, and Conversations in particular, make a great UnifiedPush Distributor. That is correct! However, you might find yourself in a dilemma: you already have an XMPP account with a trusted provider that you want to keep using, and you don’t want to host and maintain your own XMPP server. Furthermore, you may not feel comfortable using the publicly accessible Rewrite Proxy up.conversations.im. Luckily, you can easily self-host just the Rewrite Proxy component (the part that would otherwise default to up.conversations.im in Conversations).

Hint: If you are already self hosting an XMPP server stop reading and just configure the respective modules for Prosody and ejabberd.

Requirements

This guide assumes you have a relatively clean install of Debian 13. However, using other Linux distributions or integrating this into an install with existing services will probably work as well. The only other requirement is that you have a domain. Any subdomain over which you control the A record will do. In this tutorial, we will use the domain ntfy.stdmsg.tech (be sure to replace it with your own).

Setup

Installation

We are installing Prosody IM, the community modules and certbot (to get certificates).

apt install prosody prosody-modules lua-unbound certbot

Configuration

We will be creating a mininal configuration file for Prosody, usually a regular XMPP server, that has everything disabled, except for the stuff that is required to host the UnifedPush rewrite proxy.

Replace /etc/prosody/prosody.cfg.lua with this configuration:

pidfile = "/var/run/prosody/prosody.pid"

modules_enabled = {
	"tls";
	"dialback";
	"http";
	"unified_push";
}
modules_disabled = {
	"c2s";
	"offline";
}

log = {
	info = "prosody.log";
	error = "prosody.err";
}

certificates = "certs"

-- a list of accounts that are allowed to use the UnifiedPush Rewrite Proxy.
-- Can be accounts or entire domains.
unified_push_acl = {
	"user@example.com";
	"example.net";
}


VirtualHost "ntfy.stdmsg.tech"

Replace user@example.com and example.net with your XMPP account(s).

Getting certificates

We are using certbot to retrieve Let’s Encrypt certificates. If you already have a certbot setup make sure to change the --standalone parameter to something more suitable.

certbot certonly --standalone  -d ntfy.stdmsg.tech
prosodyctl --root cert import /etc/letsencrypt/live/

This assumes you have the A record of your domain pointed to your server.

Check and first start

You can use prosodyctl check to check over the configuration file. Afterwards restart Prosody with the new configuration file.

systemctl restart prosody

You can use ss -ltnp to check if Prosody is running on port 5269 (XMPP S2S) and port 5281 (The HTTPS port used to deliver WebPush notifications to the Rewrite Proxy).

Client-side setup.

In Conversations go to Settings -> UnifiedPush Distributor.

Under XMPP Account select the account you want to use. It must match what you put into the unified_push_acl earlier. Under Push Server put said domain (ntfy.stdmsg.tech in our example).

Use the UP-Example App from F-Droid to verify your installation is working correctly.

Additional information

You can also configure the built-in HTTP server and/or put it behind a reverse proxy. If you are putting Prosody behind a reverse proxy, make sure to add a correctly configured http_external_url variable to your configuration file.

For a more permanent setup, you might want to set up a certbot hook that reimports fresh certificates into Prosody.

If you are curious about how UnifiedPush works in general, I gave a talk at FOSDEM 2026 about UnifiedPush.

Sending messages via HTTP REST API

The not-so-secret goal of this tutorial is to give you a full replacement for recently-turned-into-AI-slopware1 ntfy.sh. However, ntfy.sh isn’t just a UnifiedPush Distributor (which we have successfully replaced via the guide above); it also provides a convenient way to send status and system messages via the command line, or more specifically, via HTTP. Luckily, XMPP can do that too.

I wrote a standalone guide for that called Sending Jabber/XMPP Messages via HTTP, which also uses a minimal Prosody configuration. Both setups can easily be combined into one. Just make sure that your modules_enabled section looks like this in the end:

modules_enabled = {
	"tls";
	"dialback";
	"http";
	"unified_push";
	"admin_shell";
	"post_msg";
}