# receiving email / inbound smtp

**URL:** <https://community.fly.io/t/receiving-email-inbound-smtp/1033>\
**Category:** General\
**Created:** [April 2, 2021, 2:45pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033 "2021-04-02T14:45:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![simon-weber](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/simon-weber/32/659_2.png) [@simon-weber](https://community.fly.io/u/simon-weber)\
**Post date:** [April 2, 2021, 2:45pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/1 "2021-04-02T14:45:40Z")

</div>

Hi! I’m interested in processing incoming email on fly for [a project of mine](https://www.noreply.zone). I’m picturing the fly app handling smtp + email parsing, then sending messages to a separate http service for handling. This seems like a convenient way to run the smtp part, and lets me keep my http server behind cloudflare (compared to also handling smtp on that server and pointing an unproxyed mx record at it). I gave this a shot with the experimental config mentioned in [Custom TCP ports](https://community.fly.io/t/custom-tcp-ports/497) but couldn’t seem to get anything through on port 25.

Obviously, smtp and 25/465/587 are kind of fraught, so I’m not surprised this doesn’t work. Going by [Custom inbound TCP port & long living connections](https://community.fly.io/t/custom-inbound-tcp-port-long-living-connections/794) is it possible it would work in the future?

If that’s the case, I’d also be interested to hear your thoughts on how tls would work. I imagine I wouldn’t be able to use the tls handler because of starttls, so either I’d need to provision certs myself or somehow access the ones that fly provisions.

---

<div class="post-metadata">

**Author:** ![kurt](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/kurt/32/43757_2.png) [@kurt](https://community.fly.io/u/kurt)\
**Post date:** [April 2, 2021, 3:58pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/2 "2021-04-02T15:58:35Z")

</div>

I think we should open the SMTP ports for inbound email processing. We’re about halfway done allowing all ports, but we can look at doing these early. This sounds like a cool app to me!

TLS is interesting, I honestly don’t know much about TLS for SMTP. _Assuming_ mail servers do SNI properly, it may just work with our TLS handler. If that doesn’t work, you will need your own certs. We’ve had requests for API access to the certs we manage but haven’t tackled that yet.

---

<div class="post-metadata">

**Author:** ![emily](https://avatars.discourse-cdn.com/v4/letter/e/8c91f0/32.png) [@emily](https://community.fly.io/u/emily)\
**Post date:** [April 30, 2021, 5:18pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/3 "2021-04-30T17:18:20Z")

</div>

I’m also working on something that involves receiving inbound SMTP and would love to be able to accept connections on port 25.

---

<div class="post-metadata">

**Author:** ![kurt](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/kurt/32/43757_2.png) [@kurt](https://community.fly.io/u/kurt)\
**Post date:** [May 2, 2021, 8:11pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/4 "2021-05-02T20:11:44Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![emily](https://avatars.discourse-cdn.com/v4/letter/e/8c91f0/32.png) [@emily](https://community.fly.io/u/emily)\
**Post date:** [May 4, 2021, 11:13pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/5 "2021-05-04T23:13:20Z")

</div>

That would be awesome. Looking forward to it.

---

<div class="post-metadata">

**Author:** ![kurt](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/kurt/32/43757_2.png) [@kurt](https://community.fly.io/u/kurt)\
**Post date:** [May 5, 2021, 6:00pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/6 "2021-05-05T18:00:05Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![emily](https://avatars.discourse-cdn.com/v4/letter/e/8c91f0/32.png) [@emily](https://community.fly.io/u/emily)\
**Post date:** [May 5, 2021, 6:25pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/7 "2021-05-05T18:25:27Z")

</div>

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`

Whereas:  
`telnet gmail-smtp-in.l.google.com 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?

---

<div class="post-metadata">

**Author:** ![steveberryman](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/steveberryman/32/48_2.png) [@steveberryman](https://community.fly.io/u/steveberryman)\
**Post date:** [May 5, 2021, 6:57pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/8 "2021-05-05T18:57:42Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![emily](https://avatars.discourse-cdn.com/v4/letter/e/8c91f0/32.png) [@emily](https://community.fly.io/u/emily)\
**Post date:** [May 5, 2021, 7:06pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/9 "2021-05-05T19:06:54Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![simon-weber](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/simon-weber/32/659_2.png) [@simon-weber](https://community.fly.io/u/simon-weber)\
**Post date:** [May 6, 2021, 1:43am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/10 "2021-05-06T01:43:55Z")

</div>

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 myapp.fly.dev --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](https://community.fly.io/t/access-to-the-raw-tls-certs-via-the-fly-api/985) would also solve this, I think.

---

<div class="post-metadata">

**Author:** ![kurt](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/kurt/32/43757_2.png) [@kurt](https://community.fly.io/u/kurt)\
**Post date:** [May 6, 2021, 2:57pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/11 "2021-05-06T14:57:57Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![simon-weber](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/simon-weber/32/659_2.png) [@simon-weber](https://community.fly.io/u/simon-weber)\
**Post date:** [May 14, 2021, 9:20pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/12 "2021-05-14T21:20:32Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![kurt](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/kurt/32/43757_2.png) [@kurt](https://community.fly.io/u/kurt)\
**Post date:** [May 16, 2021, 5:21pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/13 "2021-05-16T17:21:13Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![mikeb](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/mikeb/32/656_2.png) [@mikeb](https://community.fly.io/u/mikeb)\
**Post date:** [May 22, 2021, 1:23am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/19 "2021-05-22T01:23:47Z")

</div>

Just investigating this, and noticed that the client IP (eg 93.187.216.173) 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 ?

---

<div class="post-metadata">

**Author:** ![thomas](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/thomas/32/40188_2.png) [@thomas](https://community.fly.io/u/thomas)\
**Post date:** [May 22, 2021, 1:28am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/20 "2021-05-22T01:28:11Z")

</div>

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

[https://fly.io/docs/reference/services/#proxy-protocol](https://fly.io/docs/reference/services/#proxy-protocol)

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

---

<div class="post-metadata">

**Author:** ![mikeb](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/mikeb/32/656_2.png) [@mikeb](https://community.fly.io/u/mikeb)\
**Post date:** [May 22, 2021, 1:47am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/21 "2021-05-22T01:47:54Z")

</div>

A good stab! works like a charm with postfix/postscreen [Postfix Configuration Parameters](http://www.postfix.org/postconf.5.html#postscreen_upstream_proxy_protocol)

---

<div class="post-metadata">

**Author:** ![thomas](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/thomas/32/40188_2.png) [@thomas](https://community.fly.io/u/thomas)\
**Post date:** [May 22, 2021, 1:49am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/22 "2021-05-22T01:49:19Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![jsierles](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/jsierles/32/595_2.png) [@jsierles](https://community.fly.io/u/jsierles)\
**Post date:** [June 10, 2021, 9:05am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/23 "2021-06-10T09:05:24Z")

</div>

> [@kurt](#):
>
> I wonder why we restricted secrets to that size. We’ll see if we can up that limit tomorrow.

Has the limit been raised?

---

<div class="post-metadata">

**Author:** ![simon-weber](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/simon-weber/32/659_2.png) [@simon-weber](https://community.fly.io/u/simon-weber)\
**Post date:** [January 15, 2022, 12:35am UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/24 "2022-01-15T00:35:45Z")

</div>

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](https://community.fly.io/t/new-feature-every-public-ports-are-now-allowed-for-tcp-services-this-means-http-too/3624) 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:

```auto
[[services]]
  internal_port = 25
  protocol = "tcp"

  [[services.ports]]
    port = 25

  [[services.ports]]
    handlers = ["tls"]
    port = 465

  [[services.ports]]
    port = 587

```

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

---

<div class="post-metadata">

**Author:** ![user99](https://sea1.discourse-cdn.com/fly1/user_avatar/community.fly.io/user99/32/3371_2.png) [@user99](https://community.fly.io/u/user99)\
**Post date:** [January 28, 2022, 8:51pm UTC](https://community.fly.io/t/receiving-email-inbound-smtp/1033/25 "2022-01-28T20:51:24Z")

</div>

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 🙂

[Next page](https://community.fly.io/t/receiving-email-inbound-smtp/1033.md?page=2)
