There is a category of dependency that is easy to stop noticing: the one that works. It sends the emails, it collects the errors, it holds the credentials in CI — and because nothing is broken, nobody reads the invoice or the privacy policy.
In one week in June we removed three of them.
The newsletter: off Beehiiv
/api/send no longer talks to a third-party newsletter tool at all. It calls our own newsletter service, and newsletter-os is the system of record for subscribers — it owns both persistence and the welcome email. The template lives in our repository as a plain HTML string with {{firstName}} placeholders, and it is registered with newsletter-os on the first subscribe rather than configured in someone's dashboard.
The part people underestimate about leaving a newsletter provider is not the sending. It is the list. Subscriber state that lives in a tool you do not run is a migration you are choosing to do later, under worse conditions.
Error tracking: our own backend
The browser client posts to a proxy route inside our own app, which translates the client library's envelope into the shape our error-tracking backend expects and attaches its auth server-side. Server-side code posts to the backend directly — no proxy hop, because there is no browser to keep a secret away from.
That is more moving parts than Sentry.init() and a DSN. What it buys is that the error data, the retention window and the query interface are all ours, and the cost is flat instead of per-event. What it costs is that when error tracking misbehaves, there is no support contract — the person who fixes it is the person reading this.
CI: dropping credentials for a place we no longer deploy
The smallest change of the three is the one worth the most attention. Our CI still carried a Vercel bypass token, and the smoke tests still ran against a Vercel deployment — long after the production site had moved to our own host. Removing the bypass and pointing the smoke tests at the Hetzner deploy took one commit.
Nobody had been fooled by it yet. The danger was structural: two deployment targets, one of them real and one of them remembered, with CI silently validating the one that did not matter. A green build against the wrong host is worse than a red one, because it is the kind of green that gets trusted.
Why bother
Two reasons, neither of them ideological. The first is that a provider is a credential, a rate limit, a pricing page and an outage that is not yours to fix — four surfaces per provider, multiplied by every product in the fleet. The second is that every one of these systems was already something we needed to understand: we were already reading the email template source, already classifying the errors, already debugging the CI. We were doing the work and renting the result.
What it costs
Self-hosting is not free, it is just differently expensive. We now own deliverability, and a bounce that a vendor used to absorb is now a support ticket in our own inbox. Error tracking has no vendor to escalate to. And there is a real failure mode where a self-hosted component silently does less than the product it replaced, because nobody wrote the comparison down.
What's Next
The same instinct pointed at the database next, and that turned out to be the largest piece of work in the quarter — four products, three weeks, and one migration we had to abandon halfway. That is the next post.
