Mobile notifications—particularly on Android—are challenging to implement. There are 3 methods for app to send notifications in the background:
Firebase Cloud Messaging-
UnifiedPush
proprietary long‑polling connection.
For battery‑life considerations, the proprietary long‑polling approach is undesirable. Consequently, we will concentrate on a push‑based architecture that employs a notification distributor and server. We are eliminating Firebase Cloud Messaging from our sight due to the Google dependency and privacy controversy, and have the crosshair directly on UnifiedPush.
Here’s a flow chart for how notifications work in Matrix via Sunup (bare with my poor calligraphy please!):
You can see that, in the scope of Matrix notifications from clients like Element X, a message is generated passing the following flow:
The homeserver (Synapse, Dendrite, Continuwuity) processes events constantly. For each incoming event the server evaluates the configured push rules, (specifically the m.mentions entries, see Intentional Mentions MSC), to determine whether the event satisfies any rule. If a match is found, the server triggers the corresponding push notification workflow.
When an event matches a push rule, the homeserver sends a notification to the Matrix Push Gateway, which is common-proxies in our case.
The Matrix Push Gateway (Common Proxies) rewrites received notification data, and sends it to user-specified push endpoint (autopush autoend endpoint).
After receiving the notification data. The push endpoint notifies a push server (autopush autoconnect) about this new notification via Valkey.
Side note here: If you are using regular UnifiedPush applications, this is the starting point. Matrix does not support UnifiedPush natively so we need a translator.
The push server sends down notification data to your phone, to Sunup.
Sunup broadcasts the pertinent message to the target application, brings the application to the foreground, and then allows it to retrieve the required data and issue the final Android notification.
We selected Sunup because Ntfy does not function correctly on our Android 16 phones. It fails to wake the target application, preventing reliable notification delivery.
You see the notification.
Self Hosting
Before reading
Replace every domain name that appears in the article with your own domain.
Text enclosed in [] indicates a placeholder you must fill in yourself
I’m using a package to store configurations. Please use /etc rather than /usr if you’re not.
This article assumes you have basic levels of knowledge of systemd, and is using a FHS-compliant system (i.e., not NixOS). Adjust file locations accordingly if your system uses a different layout.
Required Server Packages (Arch Linux)
Package
Purpose
valkey
In‑memory key‑value store used by the autopush infrastructure.
autopush‑rs‑p1gp1g‑git
Actual push server, with Redis support
common‑proxies
Matrix Push Gateway
pwgen
Generates random passwords for configuration files.
portable(optional)
Provides isolated shell; install on a workstation rather than the production server.
Note that p1gp1g’s fork of autopush-rs is required because upstream hasn’t merged Redis support, and we don’t want to rely on Google BigTable. Valkey is the replacement of Redis.
If you aren’t using Arch Linux, those build scripts are publicly available either on Arch GitLab or AUR
Valkey
Valkey acts like a middleman. It helps autopush autoend and autoconnect to communicate.
Copy an example Valkey configuration file. Note: when you change configurations, make sure there’s no default entry overriding your config.
You may want to disable Valkey’s snapshot, and instead write everything to disk for stability. See Documentation: Persistence for more info.
We are just gonna disable RDB and enable AOF here:
1 2 3 4 5
save "" appendonly yes appendfilename "appendonly.aof" aof-timestamp-enabled no appendfsync no
Next, generate a password for your autopush-rs server:
1
pwgen -s 64 1
Copy it down, save it in a secure place.
Add autopush user to Valkey:
1
user autopush on +@all -DEBUG ~* >[your DB password here]
Final step: you can install the configuration file to /usr/share/serverOS/valkey.conf. Mind the permission should be 0700 and owned by root.
And now, create the systemd service /usr/lib/systemd/serverOS-valkey.service for Valkey with the following contents:
That’s it. You then obtain TLS certificates, restart Nginx and set the push server in Sunup. And notifications will be working, well, if you did the right thing.