Back to News
Advertisement
ccraigmccaskill 1 day ago 56 commentsRead Article on github.com
Introducing Posthorn, a self hosted email gateway. One docker container (or Go binary) between every self hosted app on your VPS and your transactional email provider. Set up Posthorn once, point your apps to it, done.

I was trying to deploy Ghost on a DigitalOcean droplet and found that DO and many different VPS services have started to block the default SMTP ports to try to combat the various types of abuse they get. To actually configure my app, I had to hack together a Postfix relay.

In another project, I had a static site which had a contact form, but my free Formspree account was occasionally hitting usage limits and I desperately wanted some of the anti-spam features they had gated behind their paid accounts so I put together a caddy module to catch HTTP POSTs and bounce them to my provider.

I kept bumping into these same email issues. Many of the services I wanted to host (Gitea, Mastodon, Umami, Comentario) ran into the same limitations. This felt like a really common issue that had no good solution.

Posthorn is what I built to solve this. It's a small Go binary (or 10 MB docker image) that sits between your self hosted apps and your transactional email provider of choice (shipping with support for Postmark, Resend, Mailgun, Amazon SES or an outbound SMTP relay). It also accepts POSTs from HTML forms to support static site needs while adding security layers such as honeypot fields, origin checks and IP rate limiting. There's also a JSON HTTP API that supports Bearer auth for backend scripts or cron jobs that just want a /send endpoint.

I now use this personally in multiple scenarios and I've spent a lot of time beating this up and testing against what I can validate. I'd love to hear how this might be useful for you, what breaks and any feedback you might have. It's open source under Apache 2.0 and I'd love contributions. I'm planning to support and grow this for the long haul.

Code: https://github.com/craigmccaskill/posthorn

Docs: https://posthorn.dev/

Longer write up: https://craigmccaskill.com/introducing-posthorn/

Previous HN discussion on the exact issue I'm trying to solve: https://news.ycombinator.com/item?id=43620318

Advertisement

⚑ Community Insights

Discussion Sentiment

82% Positive

Analyzed from 2630 words in the discussion.

Trending Topics

#email#mail#self#posthorn#server#own#services#https#support#something

Discussion (56 Comments)Read Original on HackerNews

cuu508β€’1 day ago
Confusing title, "self-hosted mail" and "self-hosted email gateway" are two quite different things :-/
craigmccaskillβ€’1 day ago
Fair. I was trying to convey self hosted benefits without the downside of hosting your own MTA. I was also really in my head on the niche and assumed everyone else would get it from the snappy title. Not my intent to mislead.
SoleilAbsoluβ€’about 12 hours ago
My eyes first Spoonerized the title to "Hostporn, selfhosted porn gateway". I didn't get a full night's sleep last night...
Maledictusβ€’about 3 hours ago
buibuibuiβ€’about 1 hour ago
This solves an issue that I really do have. Will try it out!
graemepβ€’1 day ago
The title is inaccurate. Its a self-hosted transactional email gateway, not self hosted email
radiospielβ€’1 day ago
An interesting combination of features.

Personally, I have used nullmailer in the past to provide a sendmail compatible local install that immediately forwards email to the SMTP server of my choice. Has worked flawlessly.

Obviously, that doesn't come with HTML form support, but then I am also not sure I would like the same binary to handle both a HTTP(S) endpoint and email submission :)

craigmccaskillβ€’1 day ago
Nullmailer's a good call for a single-app use case. It's basically what I was doing.

Posthorn ended up the way it did because I had three different things all hitting Resend at the same time: a contact form, a couple of apps that only had SMTP email support and some scripts I wanted to email results from. I didn't want to have to maintain three different things doing functionally the same routing. Putting them in one binary helped me consolidate credentials and logs.

You're not wrong about the split though, I thought about breaking the two out. I'd originally written the http form handler as a caddy module (which I called caddy-formward to be cute) but ultimately I went the other way because the code after the ingress is the same regardless of how you come into the service and I didn't want to rewrite all that logic.

Have you encountered a similar issue with multiple apps where nullmailer hasn't been enough? Curious how you handled it if so.

basemiβ€’1 day ago
Nice project, nice initial subset of options.

At work I'm using Apprise (https://appriseit.com/) to deliver notifications.

Are you planning to add more services or to limit Posthorn to emails?

craigmccaskillβ€’1 day ago
Haven't used Apprise, but it looks interesting!

My current plans are for Posthorn to stay focused on email. There's enough work here that I think it justifies a dedicated tool.

I have some v2 roadmap ideas for things like multiple outputs per endpoint so that a contact form submission can fire both an email and a webhook in the same call to support things like form -> email + slack or script triggers an email + pager duty alert.

That's complimentary to the email though, not something I had planned to build out as a stand alone use case. I'd be interested in hearing how that would be useful for you though!

basemiβ€’1 day ago
We've chosen Apprise just for the big number of services supported, actually at the moment we only use email and telegram as notification output channels.

Apprise does not accept SMTP protocol as input, so you're bound exclusively to API, binary exec or third party integrations.

I think Posthorn could fill a gap if it will integrate the possibility to send a webhook (alongside/instead of email).

craigmccaskillβ€’about 8 hours ago
Awesome. This is helpful, thanks. You're doing my market research for me!

Seems like most setups only really need 2-3 channels, rather than the ~140 supported by Apprise. Definitely worth me looking into when I start to work towards a v2 release and supporting webhooks in general.

The part I'm not sure about yet is how I'm going to implement it. My current approach is to custom write a transport layer for each provider, so a generic webhook would be the first non-bespoke transport. I don't want to lock myself into the path of writing a bespoke solution for every email provider and every channel (that's basically reimplementing Apprise in Go with better email support). I'm hoping I can get a generic webhook that would be good enough for most use cases.

I'll also need to think about if I want to support an email + webhook use case or pick one per request. If I built that and you deployed it for email + telegram, would that be good enough for your use case? You could also just deploy it alongside Apprise if you have an email need that we handle better (and I think we definitely do, especially once I ship html email in 1.x).

If you do end up using Posthorn, please let me know how it goes and feel free to open up any issues you see on GitHub.

ALLTakenβ€’1 day ago
I really want to try this, but I'm afraid my DNS will be blacklisted if I do. Can someone guide me and others, if this is the case? E-Mail is the most complex of everything I know in sysadmin/DNS/Server stuff.

My current provider since almost two decades without any issues, except speed and storage limitations is all-inkl.com, but I really just use it for email and nothing else, therefore most likely overpriced at ~6€/month.

I would love to switch to some VPS/root or anything where I can SSH and install, compile my own services, but something where security is high and support is 24/7 available.

craigmccaskillβ€’1 day ago
Two things to unpack here:

1) Posthorn doesn't host email - no inbox, no IMAP so it doesn't replace what it sounds like all-inkl is doing for you. All it does is take the outgoing messages from any of your hosted/local apps and take care of the plumbing of handing them off to a transactional provider (like Resend or Postmark). Those servers are the ones sending the mail, using their IPs and their sending reputation. Any blacklist concern is really tied to your sending domain and not a new risk from Posthorn. Just the same setup you'd do if you were calling something like Resend directly. If you're following their guidelines, you'll be fine.

2) On the VPS side, if your goal is to be able to ssh in, install some stuff and run your own services, something like Hetzner is a well regarded EU centric option with solid technical support baked in. Security is mostly on you and down to what you install and how you configure it. That can be a huge learning curve and a whole other kettle of fish, definitely not without risk.

dspillettβ€’1 day ago
> therefore most likely overpriced at ~6€/month

> something where security is high and support is 24/7 available

You are not going to get great 24/7 support inside anything like €6/month (though many may promise it!), so that service may not be as overpriced as you think.

Another thing to consider is that many VPS providers have β€œdirty” IP ranges so you will have trouble with getting your mail delivered. A common solution to this is to use a mail delivery service (like mailgun, mxroute, and many others) so your mail comes from a managed β€œclean” source and has less chance of summarily being declared spam just from the source address alone (these services also reduce the need to think about spf/dkim/etc and keep an eye out on changes that may happen in that realm). Essentially by self-hosting a service like this, you are taking back the admin of managing all that so you have to ask yourself if it is worth saving a few € per month, and consider what you might lose if something goes wrong and all your mail starts bouncing.

> switch to some VPS/root or anything where I can SSH and install, compile my own services

Be wary of most really inexpensive options here. In most cases you will experience performance degradation (at random times, or just almost always) and other issues due to noisy neighbours on oversold infrastructure.

> I really want to try this, but I'm afraid my DNS will be blacklisted if I do.

The best (but unfortunately time-consuming) way to get comfortable with this is probably to register a sacrificial domain and set it all up for that domain on hosts not associated with your domains that normally send mail. Play with it, break it, fix it, break it again, etc., until you feel confident you aren't going to screw yourself by trusting your own admin of all this on a domain that actually matters.

graemepβ€’1 day ago
A VPS will not be cheaper - but you may get more for your money (more storage, unlimited accounts).

For something simple try https://mailinabox.email/

> would love to switch to some VPS/root or anything where I can SSH and install, compile my own services, but something where security is high and support is 24/7 available.

Those sound like expensive requirements to me. You want managed self hosted email? Some else providing support will be expensive.

npodbielskiβ€’1 day ago
> Nobody wants to self host email server.

I do. Though I am self hosting it to have my personal email, being well... personal. Not for my company so maybe I am not the target.

Interesting project though. I always felt missing API to just send emails from some script in my mail server.

craigmccaskillβ€’1 day ago
Personal mail is the one case I think where hosting your own MTA still makes sense when you want to own the addresses and the data. You still have to solve for deliverability, which is something I hope to never have to do.

Posthorn is built for the opposite end of that, you've already decided you want to use a transactional provider for app mail and you just want to stop having to deal with wiring it into all of the things. Obviously for a big production app you build your own mail service, but for gluing together a bunch of different apps you're self hosting, I think this makes sense and addresses a real issue.

If you want an API piece to augment what you already have, Posthorn might still be useful regardless of how the rest of your mail is set up. A Posthorn JSON endpoint is just a POST with Bearer auth and an idempotency key. Example from my docs:

curl -X POST https://posthorn.yourdomain.com/api/transactional \ -H "Authorization: Bearer $WORKER_KEY_PRIMARY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: reset:user-123:$(date -u +%FT%H)" \ --data '{ "to_override": "bob@example.com", "subject_line": "Reset your password", "message": "Click here: https://app.example.com/reset/abc" }'

Could run alongside your existing mail server. It's a small enough overhead that the juice might be worth the squeeze.

ALLTakenβ€’1 day ago
> […]You still have to solve for deliverability, which is something I hope to never have to do. […]

This is the exact case where I'd be really afraid of running it on my own and this I VERY STRONGLY BELIEVE should NOT be the case. Participating in email should be easy.

npodbielskiβ€’about 17 hours ago
Yeah I looked throught that but could not sent response to your comment because for some reason HN decided I am posting to fast. Yeah I was doing it manually and sending 2 comments a day. FFS.

Anyway yeah it looks interesting. In theory I could start it up with my mail server, which already is using docker compose, configure it and use API for sending emails.

I will give it a try.

craigmccaskillβ€’about 8 hours ago
Yeah, this is a good point I should probably make a bigger deal out of in the documentation. I also have it automated with docker compose/ansible so it just deploys with the apps that need it and starts working.

Let me know how it works out for you, happy to help in any way I can. Also feel free to make any feature requests or open an issue on GitHub.

adrian_bβ€’about 18 hours ago
Any UNIX-derived operating system has some kind of command-line utility for sending email messages (named mailx or mail or Mail).

I do not remember if I have ever used the GNU mail program that is available on Linux, but my e-mail server is hosted on FreeBSD, so I have used very frequently the FreeBSD CLI utility to send e-mail messages from scripts.

Moreover, all FreeBSD servers come configured by default to send automatically a variety of e-mail reports to the administrator, to inform about incidents or statistics, so there are a lot of examples of usage from scripts.

npodbielskiβ€’about 17 hours ago
Yes I know I am using it. I just think API, built-in API should be standard. Creating separate mailbox for each self hosted service I am running and testing it and making sure it is using TLS instead of STARTTLS or other way around and fixing user name because some services send using domain and some not, and setting correct port because 465 did not work even if it should be using SSL... It is so much hussle. Maybe it was fun when I was doing PHP forms 20 years ago but not now. Now I want to have just API with /mail/send and API key and forget about all of that.
brocklobstaβ€’about 14 hours ago
How does this differ from using protonmail-bridge? Seems like a similar concept.
craigmccaskillβ€’about 8 hours ago
If I'm understanding it right, protonmail-bridge exposes an IMAP and SMTP interface to your local mail client so that if you want to use something like Outlook or Apple Mail to talk to ProtonMail you can.

Similar idea in that it receives SMTP and transforms it to a provider specific API but it's unique for the ProtonMail use case of bridging to your personal mail client and it handles both sending (SMTP) and receiving (IMAP).

Posthorn is server side and outbound only. It runs in your stack and enables apps to send email that otherwise can't. It fronts multiple transactional providers via config and doesn't touch inbound at all.

peter_retiefβ€’1 day ago
I am running a local mail server using cloudflared tunnels and brevo for sending

postfix/sendmail/dovecot/ingress setup

I am really happy with the setup. (So far)

craigmccaskillβ€’1 day ago
Nice. Sounds like a neat way to get the best of all worlds. Self hosted email without exposing your IP, leverage a third party with an existing trusted send reputation but still maintain full ownership of the stack.

How much effort has it been to keep it running? Glad that it works for you!

peter_retiefβ€’1 day ago
It has been running for about 20 days, have a small companies email and web site running locally.

Would like to publish the setup so others can try it out as well.

A fun thing was to route local domain emails locally so they never have to leave the server.

Very little maintenance so far, mustn't speak to soon though.

throwaway81523β€’1 day ago
Is Posthorn a reference to W.A.S.T.E.?
47282847β€’1 day ago
Not OP but I read it as reference to just https://en.wikipedia.org/wiki/Post_horn
chuckadamsβ€’about 23 hours ago
I initially read it as "Host Porn"
craigmccaskillβ€’1 day ago
Not intentionally, but TIL that this turned into an apt reference. The pynchon connection is excellent.

My (intentional) reference was to the older mail courier horn.

Advertisement
singpolyma3β€’about 22 hours ago
"Not a mail server"... supports SMTP in and out
standbymeβ€’about 22 hours ago
haha
ranger_dangerβ€’1 day ago
Don't services like SES already operate over 443/TLS and aren't blocked?
craigmccaskillβ€’1 day ago
Correct, but not all apps can talk directly to an HTTPS API. Ghost, Gitea, Mastodon, NextCloud, Authentik, Matrix to name a few all only have built in SMTP support. Posthorn listens for that connection from those apps locally and translates it into whatever your transactional mail provider needs.

If all the apps you're running can already integrate via HTTPS API, Posthorn doesn't solve anything for you in that case, unless the unified credential, single retry policy and logging meaningfully simplifies things for you.

And honestly, SES was the easiest integration for me to write (even if it ended up being the most LOC), their documentation, examples and error responses gave me a really easy time setting it up. Additionally, because it does need such a verbose implementation SES ends up being a great case study for Posthorn and not needing to maintain the same 200 line signing routine in multiple different places.

EMAIL36245β€’about 22 hours ago
email.riamu.io comes to mind
crimsonnoodle58β€’1 day ago
> Nobody wants to run a mail server in 2026.

We do, and thats why we use Postal [1].

The more SaaS applications that self-host email the better. It forces the big guys, ie Microsoft, to improve their blocklists and not lazily block entire ranges. Yes its work contacting them occasionally, but it keeps the internet open. The alternative is an internet where they control it all.

1. https://docs.postalserver.io/

craigmccaskillβ€’1 day ago
Fair. Don't disagree with anything you're saying here.

I should probably tighten up that line. What I really meant to say is that the average self-hoster who just wants to enable a few services to send email doesn't want to run a mail server. Different audiences, different (and both correct) answers.

I set out to solve some pretty specific problems of my own but I'm genuinely curious how others have tackled these things. Posthorn and Postal don't compete in my head. Postal makes you into your own provider, which is something I personally deeply want to avoid. Posthorn assumes you've already picked a provider (which might be Postal, actually, it would work just fine pointed at a self-hosted Postal instance).

embedding-shapeβ€’1 day ago
> the average self-hoster who just wants to enable a few services to send email doesn't want to run a mail server

Maybe I'm confused, maybe the label "self-hoster" is broader than the definition in my mind, but that's exactly what self-hosters want to do, that's why we call ourselves self-hosters, we want to host the stuff we use ourselves :)

If I just wanted to "enable a few services" I'd use AWS or whatever the modern alternatives are.

tclancyβ€’1 day ago
I think you are confused. I self-host a ton of stuff. One thing I have zero interest in is hosting mail and dealing with all of the configuration and possible timebombs waiting before I can even do the single thing I want the service to do. Instead I pay like $5/month for essentially unlimited emails from any domain I control.

That said, none of it has to do with my own personal email, which has been on Gmail for long enough to drink, so we are probably talking about two different situations.

craigmccaskillβ€’1 day ago
> If I just wanted to enable a few services I'd use AWS or whatever

But then you'd be using AWS for those services too.

I think there's a meaningful gap between folks who are willing and able to self host their own applications but have decided that running their own MTA is a separate and much harder commitment. Different line, but still self-hosting in any reasonable read of the term. I've been on both sides of that line at one point, I'm trying to avoid going back. :)

justsomehnguyβ€’about 23 hours ago
> but that's exactly what self-hosters want to do

Only a sith self-hosters want to self-host absolutely everything.

singpolyma3β€’about 22 hours ago
If the self hosters runs a mail server it can receive their transactional emails to themselves and they don't even need to care about delivery to gmail
twooclockβ€’about 23 hours ago
Can't agree more. Thank you for standing up! I'm doing it for 20yrs (currently with mail-in-a-box) and only wish more people were doing it.
quality_lifeβ€’about 23 hours ago
Security is the main worry when hosting email servers, not just of the email service, but also in preventing attacks via email. Antivirus integration is available with Postal. May be it is a good idea to chain multiple antivirus solutions. Instead of just expecting that Google or Microsoft would prevent malicious email, Postal may enable better control and transparency.
gnabgibβ€’about 7 hours ago
No, this isn't a worry. Where are you sourcing this? "not just the email service, but also preventing attacks via email".. is saying the same thing, in a rather GPT style.
quality_lifeβ€’about 6 hours ago
Hey, disagree with me, if you will. Please don't say what I am saying is GPT style. Very insulting.
0x073β€’1 day ago
Does postal still use outdated rails? That's the biggest issue I have with this project as a exposed web service and mail server is for me high risk especially with outdated software.
e12eβ€’about 23 hours ago
They seem to be on 7.2, which will get security patches until August this year:

https://github.com/postalserver/postal/blob/8ef89606bc34146f...

https://rubyonrails.org/maintenance

Not great, but better than 6.x.

0x073β€’about 22 hours ago
The links shows: gem "rails", "= 7.1.5.2"

I don't know rails well, but that sounds like 7.1 (that is unsupported) and not 7.2?