Guide: Self Hosted Notifications
-
Hello fellow Cloudron-users!
With the year about to close, I was very happy to recently be able to check off an important item for me on my self hosted checklist, 'Self hosted notifications'. I wanted to share my setup with the community for anyone else that is thinking of doing something similar.
Goals
- Self hosted (on Cloudron)
- iOS app
- Easy webhook trigger
- Open source
Why?
I want to be able to receive push notifications on my phone (iOS, important to distinguish, will touch on this later), automatically or manually with ease.
Setup
I use Matrix (server) and Element (client) for the main messages system. n8n is used to setup easy to use webhooks so I can trigger a notification from most places.
Alternatives And Why I Didn't Like These Solutions
-
Slack: I used to use Slack for notifications. They have a really solid mobile application, and easy to use webhooks, crucial for automating notifications. However, Slack is closed source and proprietary, a real deal breaker for what I'm trying to go for.
-
RocketChat: RocketChat could be used instead of Matrix. I went with Matrix because of its decentralized nature and because I already had it installed and don't have RocketChat on my Cloudron. I believe RocketChat could be used as an alternative solution. The mobile application works very well (have used it previously).
-
Gotify: Gotify has been packaged for Cloudron (unofficially) and I really wanted to use this since it's built for exactly this purpose. However, given Apple's tight grip on their hardware, Gotify doesn't have an iOS application since it requires agreeing to Apple's TOS and paying them to host an app on the App Store. Unfortunately, this was a dealbreaker for me.
- Gotify can also be used with an unofficial PushOver 'bridge' for iOS. But PushOver is closed source. Again, dealbreaker.
Guide
- Install Matrix and Element to your Cloudron.
- Install n8n to your Cloudron.
- Log in to Element using your main (LDAP) user, and create a new room that will be the place where all of your notification messages will go to, for example
#automated-messages
. In the room settings, be sure you set Notifications to 'All Messages'.
- We'll now need to create our bot user and invite it to the room created above.
- From your Cloudron dashboard, go to your Matrix application's File Manager and open the file /app/data/configs/homeserver.yaml.
- Set
enable_registration
totrue
. We'll come back and set this tofalse
later, but we need this to create our bot user. - Also set
enabled
andlocaldb_enabled
totrue
underpassword_config
. This is to ensure that newly registered users will be able to login. - Restart your Matrix application.
- Go to your Element application and register a new user. Give this 'user' (read: bot) a username that you'd want your bot to have. Keep the credentials in a secure place (probably your Cloudron's Bitwarden : ) even though you probably won't need to access this user very frequently at all.
- After the registration of the bot user, you should set
enable_registration
back tofalse
and save the config file. This way no external uninvited users can use your Matrix instance to register a new user. - Restart your Matrix application again.
- As your main user, now go to the room you created and invite your bot user.
- As your bot user, accept the invitation to the room.
- We can test your notifications proper now. Be sure you have the Element client installed on your phone and logged in as your primary user. Send a message from the bot account in the room. You should get a mobile notification! Now time to enable automation.
- As your bot user, go to your All Settings -> Help & About. Make note of your Access Token.
- If you have this token written down somewhere, you can now log out of the bot's Element account, we won't to be logged into it anymore.
- Let's create a workflow in n8n to be able to send messages with ease from our bot user! Go to n8n and create a new workflow.
- Copy the text below and paste it into your workflow.
{ "name": "Send Matrix Message", "nodes": [ { "parameters": {}, "name": "Start", "type": "n8n-nodes-base.start", "typeVersion": 1, "position": [ 240, 300 ] }, { "parameters": { "roomId": "!roomId:matrix_url.com", "text": "={{Object.keys($json[\"body\"]).length > 0 ? $json[\"body\"][\"msg\"] || JSON.stringify($json[\"body\"]) : $json[\"query\"][\"msg\"] || \"Notification triggered\"}}" }, "name": "Matrix", "type": "n8n-nodes-base.matrix", "typeVersion": 1, "position": [ 780, 120 ], "credentials": { "matrixApi": { "id": "11", "name": "Matrix Bot" } } }, { "parameters": { "httpMethod": "POST", "path": "matrix", "options": {} }, "name": "POST Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [ 240, 120 ], "webhookId": "5b81524e-74fb-4da2-9526-51eb20e4ccfb" }, { "parameters": { "path": "matrix", "options": {} }, "name": "GET Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [ 240, -40 ], "webhookId": "379725f7-f3ab-4c5e-a99b-334d70023295" }, { "parameters": { "conditions": { "boolean": [], "string": [ { "value1": "={{$json[\"query\"][\"x-matrix\"] || $json[\"headers\"][\"x-matrix\"]}}", "value2": "neo" } ] } }, "name": "IF GET", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 480, -40 ] }, { "parameters": { "conditions": { "boolean": [], "string": [ { "value1": "={{$json[\"headers\"][\"x-matrix\"]}}", "value2": "neo" } ] } }, "name": "IF POST", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 480, 120 ] } ], "connections": { "Start": { "main": [ [ { "node": "Matrix", "type": "main", "index": 0 } ] ] }, "POST Webhook": { "main": [ [ { "node": "IF POST", "type": "main", "index": 0 } ] ] }, "GET Webhook": { "main": [ [ { "node": "IF GET", "type": "main", "index": 0 } ] ] }, "IF GET": { "main": [ [ { "node": "Matrix", "type": "main", "index": 0 } ] ] }, "IF POST": { "main": [ [ { "node": "Matrix", "type": "main", "index": 0 } ] ] } }, "active": true, "settings": {}, "id": 10 }
- Be sure to create a new Credentials for Matrix using your bot's Access Token we copied from earlier and your Matrix URL, and set your workflow to use those credentials. Also be sure to update the Room ID in the workflow to the shared room created above.
- Save and Activate this workflow. This workflow enables both POST and GET requests that will send a message to your room from the bot user!
- As a precaution, we validate that the request contains a special header/parameter and only then will send the message. In this case, we want
x-matrix
to be set toneo
. If this is not present, the request will return a 200 response, but won't actually send a message/notification. - The text to send should be under the name
msg
. If nomsg
exists, we fallback to sending the text 'Notification triggered'.
- As a precaution, we validate that the request contains a special header/parameter and only then will send the message. In this case, we want
Example CURL commands to test this out from your Terminal:
- GET request with validator header:
curl "https://n8n.cloudron.url/webhook/matrix?msg=Test%20message" -H "x-matrix: neo"
- GET request with validator parameter:
curl "https://n8n.cloudron.url/webhook/matrix?msg=Test%20message&x-matrix=neo"
- Invalid GET request with no validator (returns 200 status but does not send notification):
curl "https://n8n.cloudron.url/webhook/matrix?msg=Test%20message
- POST request
curl -X POST https://n8n.cloudron.url/webhook/matrix -H 'x-matrix: neo' -H 'Content-Type: application/json' -d '{"msg": "Test message"}'
You can also simply visit a URL in your browser to send a test message: https://n8n.<cloudron_url>/webhook/matrix?msg=Test%20message&x-matrix=neo
You can now use automations to trigger a notification given the URL's and examples above!
Still Left To Figure Out
Since I want this URL to be as easy for me as possible to ping in the future, I'd like to be able to set up some sort of forwarding mechanims where
push.cloudron_url.com
goes ton8n.cloudron_url.com/webhook/matrix
. The tricky part is that the plain forwarding URL (push.cloudron_url.com) ideally would not need any path but it would forward to the path/webhook/matrix
on the actual URL. This solution would also need to forward query parameters, headers, etc with it to ensure the notification comes through properly. Open to suggestions on how to go about doing this! -
@thetomester13 said in Guide: Self Hosted Notifications:
I'd like to be able to set up some sort of forwarding mechanims where push.cloudron_url.com goes to n8n.cloudron_url.com/webhook/matrix.
That's what smarter reverse proxies do like nginx or traeffic which also help you scale the n8n backend service for the frontend push service..
If you don't need that level of scale, you can just deploy a surfer app and some JS/HTML to take a number of different GET/POST requests and forward them as you expect. This may be called URL cloaking in some circles and domain forwarding in others.
Initially I thought you were aiming at setting up a web page where you can type in any message for you/others, but that removes the API aspects which N8N is for.
-
deploy a surfer app and some JS/HTML to take a number of different GET/POST requests and forward them
True, a very reliant and low tech solution that could work! If I don't think of something else anytime soon that's probably what I'll do. I'll update this post when I have that part working as well.
Initially I thought you were aiming at setting up a web page where you can type in any message for you/others, but that removes the API aspects which N8N is for.
No, don't want anyone just being able to send me notifications whenever