Yesterday I concluded that ejabberd-for-Matrix isn’t ready, and that for plain XMPP my Prosody 13 setup is actually ahead. Fair. But the ejabberd YunoHost app still needed to exist — properly, catalog-quality — for the day that Matrix gateway bug gets fixed, and because choice is good. A half-finished packaging experiment isn’t a real option.
So today’s job was to take the app from “installs on my test box if I hold its hand” to “a stranger could install this and it just works.” The only honest way to prove that is a clean room: fresh servers, fresh domains, nobody’s thumb on the scale. I paired with Claude again, and my partner spun up two throwaway Debian boxes on Hetzner — anton.this-is-system-a.de and bieber.this-is-system-b.de, two separate real domains, DNS at Porkbun. Two domains, not one, on purpose: you can’t test federation with a single server.
The headline
From two bare Debian installs to two YunoHost servers, each running ejabberd, each with a real Let’s Encrypt certificate — and:
- Both servers score 100% on the XMPP Compliance Suite — anton and bieber — on clean installs, with zero manual fixups. Not a fluke on one box: two independent installs, same perfect score. (My own long-running server took a trail of hand-edits to get there; these just arrived.)
- A message from
alice@antonlanded inbob@bieber’s mailbox — across two domains, server-to-server, over TLS. Real federation, not a loopback trick.
That’s the whole thesis of a good XMPP server: speak the modern protocol correctly, and talk to other servers. Both, on a clean install. That’s the day’s win.
The bit I’m quietly proud of: DNS that configures itself
XMPP needs SRV records so clients and other servers can find it — including the _xmpps direct-TLS ones that basically nothing sets up for you. On my old server I typed those in by hand at the registrar, and watched them flap. This time the app declares its own DNS records, so YunoHost’s domain dns suggest just knows about them, and domain dns push sends them to the registrar. Set up a domain, push, done. That’s the difference between “installs” and “installs and configures itself.”
…except when the registrar push is broken
Which is where the day earned its bug reports. YunoHost’s Porkbun push cheerfully created the A, CNAME and TXT records — and then returned 400 Bad Request for every single MX and SRV record. Every one. On our test domains and, it turns out, on another of my servers, for all its domains.
We traced it: Porkbun’s API wants a record’s priority in its own prio field, but YunoHost hands it the priority glued to the front of the record content ("0 5 5223 host."), which Porkbun rejects. So any Porkbun user’s mail (MX) and service records (SRV — XMPP, Matrix, SIP, you name it) silently fail to publish. That’s not a niche edge case; that’s “your mail and chat DNS don’t get set up.”
So it went where it belongs — and this time with a fix attached:
- 🐛 YunoHost #2830 — the full root-cause analysis and reproduction
- 🔧 dns-lexicon #102 — a pull request that splits the priority into Porkbun’s
priofield, fixing MX and SRV
And a bonus gremlin: on Debian 13 the sury_php signing key ships readable only by root, so apt’s verifier can’t read it — which quietly breaks every app install at the dependency step until you chmod it. Not ours, but it’ll bite anyone on that release. One more to write up.
The part where the server locked me out
Now the comedy, because it wasn’t all triumphant. To do any of this, you need to get into the servers — and YunoHost, sensibly, hardens SSH the moment it finishes installing. My partner set up an admin user beforehand… as a plain Unix user, which YunoHost’s tightened SSH politely refuses. Then a pile of retry attempts tripped fail2ban, which banned us at the firewall. Then the console rescue needs a root password that a fresh cloud image doesn’t have. We reimaged. Twice.
What finally worked was almost poetic: boot the boxes into Hetzner’s rescue system, mount the disk from the outside, and hand-edit the one line in the SSH config that decides who’s allowed in — then reboot back. Off-machine surgery to reopen the front door. Every step of that saga is now written into notes so neither of us repeats it. Ops is humbling; the trick is to only learn each lesson once.
Where this leaves things
The ejabberd app is real now — not “works on my machine,” but validated on a clean install, on real infrastructure, at 100%, federating with a second server. It’s ready to put in front of the YunoHost catalog.
The running tally of things that got better because we poked at them: yesterday’s four reports about ejabberd, Debian and Prosody; today a YunoHost DNS bug and its fix, plus a Debian packaging gremlin. None of that was the goal. It’s just what falls out when you insist on testing the boring path — a fresh install, on a fresh domain — instead of the demo that always works.
Two servers. Two domains. A hundred percent. Eventually.