I Built My Own Messenger for My Fiancée, and I
It feels like every few months, there's a fresh push, from some government, some regulator, or a conveniently "concerned" tech company, to build a backdoor in the end-to-end encryption so the right people can read your messages whenever they decide they need to. Always sold to us as being for our safety, naturally. Think of the children. Think of the terrorists. Think of anything except the precedent it sets.
I don't like that direction. And it left me wondering:
How hard would it be to self-host our own messaging app?
The real thing - server included, sitting in my house, just for my fiancée and me. I already run a small home server, so I had a place to host it. Literally a few hours later, we've got our own encrypted messenger with text and video calls, running entirely on hardware I can reach out and touch in the other room.
I Kept the Requirements Short
- Works on iPhone, with push notifications.
- Messaging and voice/video calls.
- End-to-end encrypted by default.
- 100% self-hosted.
Picking the Stack: Matrix
The foundation is Matrix, an open, federated protocol for real-time communication. Anyone can stand up a server, and servers can federate with each other... or not. Mine is deliberately antisocial, federating with nobody and serving exactly two accounts.
From there, it's three pieces:
- Synapse — the reference Matrix homeserver, and the brain of the operation: accounts, rooms, and message history. Locked down on purpose: federation off, open registration off, both accounts created by hand.
- PostgreSQL — Synapse's database.
- Element X — the iOS client. End-to-end encryption by default (cross-signed devices, recovery key), and crucially, fast.
Standing It Up
Synapse itself came up fast: a container, a config file, a database, done.
Exposure + TLS
A messenger is useless if it only works on home Wi-Fi, so it has to be reachable from the internet over HTTPS with a valid certificate. I already run Nginx Proxy Manager in front of my services, so it supports TLS with a Let's Encrypt certificate and reverse-proxies through to Synapse.
Calls Were a Whole Second Project
I assumed the video would be a toggle. I tapped the call button and got:
MISSING_MATRIX_RTC_TRANSPORT
Turns out modern Matrix calling (MatrixRTC, the Element Call stack) needs an actual media server, an SFU, to route the audio and video. So I stood up LiveKit as the SFU, plus lk-jwt-service to mint access tokens by validating Matrix logins.
Messaging worked almost immediately. But video and audio calls required an additional stack. Once it was wired together, though, it just worked.
The Payoff
Now it's what we actually use day to day.
No ads. No "messages may be used to improve our services." Nobody in the middle holding the keys.
Just us, on hardware I own.
Is it more work than installing Signal or WhatsApp? Obviously.
Is it cheaper, simpler, or more reliable than a commercial service? Also no.
That was never really the point.
The point was to prove that, with modest hardware and a couple of hours, it's entirely possible to run your own encrypted communications platform.
And there's something deeply satisfying about sending a message and knowing exactly where it lives.
The Stack
- Matrix — open, federated communications protocol
- Synapse — Matrix homeserver
- PostgreSQL — Synapse's database
- Element X for iOS — the client
- LiveKit — the SFU carrying voice and video
- lk-jwt-service — issues LiveKit tokens from Matrix logins
- Nginx Proxy Manager — reverse proxy and TLS