receiving email / inbound smtp

We’re going to try to get this in for you in the next few days. These sound like really fun apps.


That would be awesome. Looking forward to it.

Happy weds! You should be able to add port 25 to your app configurations and accept email now. Give it a test and let us know how it goes.


Just gave it a try, it lets me deploy the application on port 25, but I’m unable to connect to it from the outside.

From my test machine:
telnet {ip} 25 results in telnet: Unable to connect to remote host: Connection refused

telnet 25 actually connects, so I at least know the issue isn’t outbound filtering.

I had the app previously successfully accepting mail by deploying it on port 10000 and proxying port 25 traffic to it from another server, and it’s passing the tcp healthchecks. Could the inbound port be blocked somewhere on your end?

Can you try it again please? We just noticed a small configuration error but I think it should work for you now!

It works. Thanks for opening this up, we’re really excited about using Fly!


Port 25 is looking good for me too! Thanks.

I looked into tls briefly. My understanding is that a typical expectation for mail servers is:

  • port 25: try to upgrade via starttls; server decides whether non-upgraded sessions are allowed
  • port 465: smtp over tls
  • port 587: like port 25, but require tls upgrading

The port 465 standard does seem like it could work if the port was available: I set up a tls handler on another port and got mail through with swaks --tls-sni --tlsc .... That flag was only recently added to swaks, though, so I have no idea how common it is for mail clients to actually do sni.

Ports 25 and 587 seem like they would work if I managed certs myself. Conceptually it seems simple to have something periodically write certs to a fly secret, but I’m fuzzy on the details. Doing an acme http challenge from my app on fly seems hairy since I’d need to serve smtp and http from one image, right? Or maybe I could run something externally that uses a dns challenge? Access to the raw TLS certs via the Fly API would also solve this, I think.

1 Like

It seems like SNI might work with port 465 with our built in TLS handler. Postfix sends SNI too, it might be well supported.

You don’t have to run an HTTP server on the same app, you can do DNS based verification.

I’d been meaning to set up cert-manager anyway, so I’ve now got that running outside of fly with dns challenges. I’m still working on the best way to deploy the certs.

I was planning to put the key + cert into secrets so that my external cron could run flyctl secrets set instead of a build/deploy. But, the cert (in pem format) turns out to be too big for that: it’s ~5kb and secrets are limited to 4kb. For now I’ve set it via an env var in flyctl deploy.

I think could avoid a build by finding the current image via fly’s registry and passing it to flyctl deploy --image, but that seems like a hassle. My best idea right now is to just chop up the cert into 4kb chunks and concat them at startup. Anyone got a better idea?

I wonder why we restricted secrets to that size. We’ll see if we can up that limit tomorrow.

1 Like

Just investigating this, and noticed that the client IP (eg that connects on port 25 is the edge proxy IP (I assume anyway, since it’s always the same regardless of which client I actually connect from).

Is there an option to setup a SMTP handler, like the HTTP ones, that support the SMTP proxy functionality, so that we can get the real client IP ?

Just a stab, but: getting the original source is the use case for the proxy_proto handler:

Your SMTP software would need to grok the HAProxy proxy protocol to make that work.

A good stab! works like a charm with postfix/postscreen Postfix Configuration Parameters

I’m glad this works for you but sad that this collapses a giant ambitious nightmare network feature I just proposed to Kurt.


Has the limit been raised?

To close the loop here: I finally got back to this project and everything is working beautifully! The secrets limit has been raised, and now that all ports are allowed I can run on 25, 465, and 587.

In case it’s helpful for others working on smtp, here’s the relevant bits of my fly.toml:

  internal_port = 25
  protocol = "tcp"

    port = 25

    handlers = ["tls"]
    port = 465

    port = 587

Thanks for all the improvements along the way! I’m really excited to cut traffic over from my old server.


I’d be interested in seeing your postfix config & dockerfile if you are able to share. I’m struggling right now with starttls specifically, and am wondering if you have solved that.

Thanks for sharing :slight_smile:

Sorry, can’t help there! I’m using haraka.

oh, haha. me too, I just assumed you were using postfix and was going to try to adapt it to haraka. Would you be able to share what you have for haraka? Thanks :slight_smile:

Hah, that makes it easy. I’m running more or less stock config.

tls.ini: key and cert are set
smtp.ini: listen= to match internal_port
plugins: tls is included

My dockerfile installs haraka and then runs a script that copies my cert over from secrets:

mkdir -p /var/lib/acme/
echo "${DEVNULL_KEY}" | base64 -d > /var/lib/acme/
echo "${DEVNULL_CRT}" | base64 -d > /var/lib/acme/

exec npx --no-install haraka -c /app/devnull

I’ve also since switched my fly.toml from the earlier example to use port 465 without the tls handler. This makes it do starttls with my cert rather than smtp-over-tls with fly’s. I’m not sure it really matters since that port seems to be deprecated for smtp usage.

1 Like