One of the main needs of a small business or startup is to have a reliable mail system with its own domain that helps differentiate on the Internet. Although there are lots of hosting plans offering free email accounts and even you could set up your own mail server, you are probably already used to mail services like GMail and would like to continue using it to also manage mail from your own domain without having to come to paid solutions such as G Suite (formerly Google Apps), which though inexpensive for the services you get in return, they represent an additional cost that your incipient project might not afford.
If this is your case you will be happy to know that thanks to the free tier offered by Amazon Web Services (AWS) in some of its services such as Amazon SES and Lambda, you can build a mail system at no cost that integrates seamlessly with your GMail free account, and at the same time allows you to send and receive mail from multiple mailboxes within your own domain.
Error: Your Requested widget " ai_widget-6" is not in the widget list.
- [do_widget_area above-nav-left]
- [do_widget_area above-nav-right]
- [do_widget_area footer-1]
- [do_widget id="wpp-4"]
- [do_widget_area footer-2]
- [do_widget id="recent-posts-4"]
- [do_widget_area footer-3]
- [do_widget id="recent-comments-3"]
- [do_widget_area footer-4]
- [do_widget id="archives-4"]
- [do_widget_area logo-bar]
- [do_widget id="oxywidgetwpml-3"]
- [do_widget_area menu-bar]
- [do_widget id="search-3"]
- [do_widget_area sidebar]
- [do_widget id="search-4"]
- [do_widget id="ai_widget-2"]
- [do_widget id="categories-5"]
- [do_widget id="ai_widget-3"]
- [do_widget id="ai_widget-4"]
- [do_widget id="ai_widget-5"]
- [do_widget_area sub-footer-1]
- [do_widget id="text-4"]
- [do_widget_area sub-footer-2]
- [do_widget_area sub-footer-3]
- [do_widget_area sub-footer-4]
- [do_widget_area upper-footer-1]
- [do_widget id="search-2"]
- [do_widget id="recent-posts-2"]
- [do_widget id="recent-comments-2"]
- [do_widget id="archives-2"]
- [do_widget id="categories-2"]
- [do_widget id="meta-2"]
- [do_widget_area upper-footer-2]
- [do_widget_area upper-footer-3]
- [do_widget_area upper-footer-4]
- [do_widget_area widgets_for_shortcodes]
- [do_widget id="search-5"]
- [do_widget id="ai_widget-6"]
- [do_widget_area wp_inactive_widgets]
- [do_widget id="wpp-2"]
- [do_widget id="text-1"]
- [do_widget id="recent-posts-3"]
- [do_widget id="categories-3"]
- [do_widget id="archives-3"]
- [do_widget id="icl_lang_sel_widget-3"]
The procedure below is not exclusive to GMail accounts. Any other email service that lets you send mail choosing the name and address you want to appear in the From: field will be suitable. Receiving mail from your domain does not even require any additional feature, so any email service, whether Office 365 Outlook, Yahoo or iCloud Mail will do.
IMPORTANT: Check out the limits of AWS free tier and the costs you will incur if you exceed them. Even though they are quite broad for the purpose of this article, they could result in an economic loss that was not planned. Remember that if you carry out this procedure you do it under your entire responsibility.
1. Create new DNS zone for your domain on Amazon Route 53
The first step is to create a Public Hosted Zone for your domain on Amazon Route 53 if you don’t have it yet. To do this, access to AWS console and go to Route 53 -> Hosted zones -> Create Hosted Zone. You can harness this step to add some DNS entries if you need them, such as A records that point to your website or SPF record as a security measure to prevent the email addresses within your domain from being spoofed or your messages from being considered as spam.
2. Verify your domain on Amazon SES
Open a new browser tab and go to Amazon SES service through the AWS console. Choose the Domains menu option and verify your domain, which will cause new DNS registers to be created in your Route 53 DNS zone as you can see in the following images:
IMPORTANT: Route 53 DNS zones cost $0.50/month per domain. If you don’t want to incur this expense, you can copy at this point all DNS records that appear in the last screenshot to the DNS management console of your current domain registrar or other DNS provider. If you prefer to use the Route 53 service you will have to update your current domain’s DNS servers and put in place those provided by Amazon (NS records of the last screenshot).
3. Create a Lambda function to forward incoming mail to your GMail account
This point includes two steps. In the first place you will create a Lambda function by opening the main page of Amazon Lambda service in a new browser tab and clicking on Create a Lambda function. You will select a Blank Function and skip Configure triggers step. In the screenshots below you can see the form fields that need to be filled in order to create the needed Lambda function.
Error: Your Requested widget " ai_widget-6" is not in the widget list.
- [do_widget_area above-nav-left]
- [do_widget_area above-nav-right]
- [do_widget_area footer-1]
- [do_widget id="wpp-4"]
- [do_widget_area footer-2]
- [do_widget id="recent-posts-4"]
- [do_widget_area footer-3]
- [do_widget id="recent-comments-3"]
- [do_widget_area footer-4]
- [do_widget id="archives-4"]
- [do_widget_area logo-bar]
- [do_widget id="oxywidgetwpml-3"]
- [do_widget_area menu-bar]
- [do_widget id="search-3"]
- [do_widget_area sidebar]
- [do_widget id="search-4"]
- [do_widget id="ai_widget-2"]
- [do_widget id="categories-5"]
- [do_widget id="ai_widget-3"]
- [do_widget id="ai_widget-4"]
- [do_widget id="ai_widget-5"]
- [do_widget_area sub-footer-1]
- [do_widget id="text-4"]
- [do_widget_area sub-footer-2]
- [do_widget_area sub-footer-3]
- [do_widget_area sub-footer-4]
- [do_widget_area upper-footer-1]
- [do_widget id="search-2"]
- [do_widget id="recent-posts-2"]
- [do_widget id="recent-comments-2"]
- [do_widget id="archives-2"]
- [do_widget id="categories-2"]
- [do_widget id="meta-2"]
- [do_widget_area upper-footer-2]
- [do_widget_area upper-footer-3]
- [do_widget_area upper-footer-4]
- [do_widget_area widgets_for_shortcodes]
- [do_widget id="search-5"]
- [do_widget id="ai_widget-6"]
- [do_widget_area wp_inactive_widgets]
- [do_widget id="wpp-2"]
- [do_widget id="text-1"]
- [do_widget id="recent-posts-3"]
- [do_widget id="categories-3"]
- [do_widget id="archives-3"]
- [do_widget id="icl_lang_sel_widget-3"]
The Node.js source code for this Lambda function is based on this GitHub repo: https://github.com/arithmetric/aws-lambda-ses-forwarder. The following are the only changes I had to make in that code to illustrate my example, which has been tested and works satisfactorily:
var defaultConfig = { fromEmail: "info@example.com", subjectPrefix: "", emailBucket: "example-com-mailbox", //emailKeyPrefix: "emailsPrefix/", emailKeyPrefix: "", forwardMapping: { "info@example.com": [ "example@gmail.com" ] } };
The second step revolves around modifying the security policies associated with the IAM role that is automatically generated when creating our Lambda function. You need to add two new policies: one to allow access to the S3 bucket that will be created later to store mail messages, and another one to access the resources on Amazon SES service. To do this, once the Lambda function is created, open IAM service in a new browser tab and click on the following options: Roles -> example-com-forwarding -> Policy Name AWSLambdaBasicExecutionRole-99b88501-cad9-4f48-ba03-75b13f98dae0 -> Edit Policy. In that last page add the JSON code in blue in the following screenshots:
4. Create a new email reception rule in Amazon SES
Again from the main page of the Amazon SES service, click on the Rule Sets menu option and then on the button Create a Receipt Rule, which will initiate a procedure of 4 very simple steps that will culminate with the creation of a new mail reception rule for your domain. The first of these steps will be to create one or more mail recipients. Here you can enter a single email address within your domain, or just the domain name to allow mail reception for any address.
Next create 2 actions that will be executed every time Amazon SES receives an email for your domain: store the message in an S3 bucket and then execute the Lambda function for it to be forwarded to your GMail account:
After completing these steps the mail reception rule is completely configured, but maybe it doesn’t become active and you have to go back and set it active. So once configured and activated if you send an email to the adress or addresses you created for your domain (in our example any address within the example.com domain, ie info@example.com) you will see how the message is stored as a new object in the S3 bucket you just created when selecting the first action:
5. Verify your GMail address and create SMTP user
In order for Amazon SES to accept outgoing mail from your GMail account it is necessary to verify it and create an SMTP user:
Initially your newly configured Amazon SES service will be quarantined (sandboxed) by Amazon as a measure of protection against possible abuse and spam. To remove it from quarantine and allow normal mailing you need to open a support ticket to Amazon and fullfill a request. Otherwise you will see how the emails you send bounce with the following error message:
554 Message rejected: Email address is not verified. The following identities failed the check in region EU-WEST-1: myexample@gmail.com
Below is the page from which you can request to be moved out of the sandbox and a request message example. In our example we asked for higher limits because we also want to use the Amazon SES service to send a weekly newsletter to our subscribers:
6. Configure GMail to send email from your domain’s addresses
We reached the final stretch. The only thing left is to configure GMail to send mail by putting your domain’s address as sender (From: field). In order to receive mail, nothing is to be done, as this is the responsibility of SES service and does not require any additional configuration. Between the two screenshots below there is an intermediate step that consists of entering the SMTP configuration data that was obtained in step #5:
7. Debugging and mail sending statistics
Finally, if something went wrong and you don’t get your emails properly redirected you can have error debugging information and log messages from your Lambda function provided by the CloudWatch service. You can also obtain email sending and receiving statistics from SES (see image from previous section #5) and operating statistics of your Lambda function as you can see below:
39 comments
Join the conversationCarlos Kynäslahti - 08/10/2017
Hi,
Thanks for the instructions. Unfortunately, I could not get this to work. Earlier I was able to get emails to store in my bucket, but once the Lambda script was ran, it couldn’t get access to the bucket. I re-did everything, now my emails aren’t even stored inside the bucket, they bounce right back.
These are the two issues I’m having:
The “Step 3” IAM policy summary doesn’t list the “CloudWatch Logs” for some reason, even though I have the same configuration as you in the JSON. I’m getting this message in the policy Summary page: “This policy defines some actions, resources, or conditions that do not provide permissions. To grant access, policies must have an action that has an applicable resource or condition. For details, choose Show remaining”
Another issue I ran into was with “Step 4”. After creating the bucket & lambda steps and hitting “Create Rule”, I get a message about “Missing Permissions”. My only option is to click “Add permissions”. Here is the whole message:
“Missing Permissions
SES was unable to access the resource arn:aws:lambda:eu-west-1:ID REMOVED HERE:function:carlospwk-email-lambda. It may not have the necessary permissions. Would you like SES to attempt to add those permissions on your behalf?
SES will perform lambda:AddPermission with the following configurations:
SourceAccount: MY ID HERE
Principal: ses.amazonaws.com
Actions:
lambda:InvokeFunction”
My only clue is that maybe something is not going according to plan when creating the Lambda function. I think the Lambda tool has been updated since this guide was written (the screenshot looks different).
Sorry if this message seems basic, I’m new to Amazon AWS.
Thanks,
Carlos Kynäslahti
Carlos Kynäslahti - 10/10/2017
I don’t see my previous message here, but in it I said I currently my emails bounce right back when I send something to SES. I noticed that even though I created a “Rule Set” in SES, it didn’t become active. I need to go back and set it active. After that it started working again. Consider adding a little notification about it in “Step 4”.
Carlos Kynäslahti - 10/10/2017
I _finally_ figured out what’s wrong with this. In “Step 3” there is the little config code snippet. The “emailKeyPrefix” variable no longer works if it’s just commented out. It should be empty like so: emailKeyPrefix: “”,. Once I fixed this, I started receiving email!
Edgar Vazquez - 04/10/2020
Thank you! Not sure where I originally got that code snippet from, but it had the emailKeyPrefix filled out. Once I made it a blank string, I started getting the email into the bucket and forward to my gmail!
Daniel - 11/10/2017
I’m glad it’s working for you now! I’m sorry I couldn’t help you, but I’ve been so busy these days. Thanks for your feedback!
Carlos Kynäslahti - 12/10/2017
Solving problems on your own is the best way to learn 🙂 Thank you for taking the time to write this guide!
Dan Rose - 06/12/2017
Works but not a great solution if you also want to receive mail that looks normal. Sending is flawless, but all mail received (SES forwarded on to my gmail account) shows up with correct reply-field only, but the From: field lists myself as the sender, instead of the original true sender. Huh? I’m the recipient not the sender. Apparently caused by the hack used to get SES to forward the mail in the first place.
Not very usable if you want to receive email and reply quickly and not confuse the recipient, and or lose the customers email.
Daniel - 19/12/2017
That’s true, but it’s not a problem for me as I know that emails from a given address (you can use an inexistent address from your domain, e. g. forwarded@example.com) are actually forwarded emails and I only have to push the “Reply to” button to know the true sender’s address if the name, which is not modified, doesn’t give me a clue. From the sender’s point of view, nothing changes and it is completely transparent both when writing to us and when receiving mail from us, so I think it’s a great solution given the cost, and perfectly usable.
Marbin - 13/01/2018
Hi Daniel. Thanks for all your help in this post. I just had one question. You mentioned that “From the sender’s point of view, nothing changes.” However, in my experience, when I reply to the sender, the sender sees that the origin of their email is now my own domain. As if I wrote their email. Is that the expected behavior or have I missed something?
Daniel - 15/01/2018
Yes Marbin, I’m afraid it’s the expected behavior. You have to change the sender’s email address in the mail history below your response before you send it because although it’s name is displayed correctly, the email address is replaced by the forwarding address from your domain. I admit it’s a little annoying and you can forget about it. It is a flaw in this method, which is not perfect.
Kristopher - 25/01/2019
Wanted to mention that, as per this StackOverflow post, if you make the replyTo address one that is not configured in Gmail, it will revert to using the forwarded reply to address as desired. https://stackoverflow.com/a/2429134/2521241 Hope this helps!
Suyash - 16/04/2018
Hi,
Outstanding post. Better than all those docs on aws.amazon.com
Johnny - 15/07/2018
I was stuck attempting to do this until I found this post, thank you very much on writing it. You may want to edit the //emailKeyPrefix: “emailsPrefix/” part though as a previous poster has stated. That helped me fix ‘copyObject() returned error:’ that I was getting.
willie - 27/08/2018
Daniel,
Thanks for the post. I cannot figure out step 6. You posted two pictures, but not the middle step! What exactly are the STMP Server, Username and Password?
I believe the STMP Server is the Amazon Server, but exactly which Username and password am I to use. I’ve tried so many, but all of them come back with “Authentication failed. Please check your username and password.”
Help! This is making me crazy
Daniel - 27/08/2018
“Between the two screenshots below there is an intermediate step that consists of entering the SMTP configuration data that was obtained in step #5”. Check again step #5, there is another screenshot with that username and password.
Niels Horn - 05/01/2019
Hello,
I had the same issue like you. First of all you have to add the domain to your google account!
https://support.google.com/a/answer/7502379?hl=en
When you had verified, that the domain is yours, tha the step will succed
Willie - 04/09/2018
I retried today, following the same instructions that you provided. It worked. It must have been one of those things before, where I entered a typo, or was copy and pasting in a wrong field. Thank you for your thoughtful reply and thank you once again for this great article!!
David - 06/09/2018
Amazing post! Working like charm the very first time.
Michael OConnor - 11/09/2018
Very useful post. I set up a hosted domain with Route 53 last year but didn’t have mail (MX) support set up as I didn’t want to have yet another email service to worry about or pay for. This solved my problem and is up and running for me. Thanks!
Azat - 21/09/2018
I can not finish step 6. In the second figure, the code should come, but when I check the incoming to the mailing address … @ gmail.com (this is the account to which I’m trying to attach … @ midomain.com) there is no letter. In general, even in spam(
Juliana - 14/06/2019
The same happen with me 🙁
Alberto - 12/08/2019
Please check if you manually created the MX record… I had the same problem and then after I double checked Route53 there wasn’t a MX there (please see last screenshot of step 2).
Here’s some info on how do manually create that MX record https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-mx-record.html but basically it’s adding as it is on the screenshot “10 inbound-smtp.regionInboundUrl.amazonaws.com” where regionInboundUrl is the region that you configured SES e.g. inbound-smtp.us-east-1.amazonaws.com (us-east-1 for US East (N. Virginia))
By the way, thanks Daniel for such a complete guide!
Kraulain - 19/11/2018
Hi,
Thanks very much for this.
it works like charm.
I was getting desperate and already considering paying for workmail which I consider greatly overpriced as compared to the service rendered. but this really helped me out
Abdul Hameed - 08/01/2019
I almost configured all things correctly but when adding Add a mail account step
there are 3 inputs, username, password and POP Server. I entered STMP username and password which got from SES but what is POP Server. I tried default i.e “mail.mydomain.com”, “inbound-smtp.us-west-2.amazonaws.com” but nothing worked and last error is
“There was a problem connecting to inbound-smtp.us-west-2.amazonaws.com
Server returned error: “Connection refused”
any help will be greatly appreciated
Daniel - 08/01/2019
Hi Abdul, a POP server is used to download messages from your mailbox into an email client such as Outlook, Thunderbird, etc. But there is no POP server involved in any of the steps of this post because the mail is sent and received through GMail using only the SMTP protocol and Amazon services mentioned, not downloaded using the POP protocol.
Shah - 10/01/2019
Dear Daniel
This is a great article
As you mention one or multiple ids can be used from same domain, Can you share info on how does it works when records are setup with cloudfare or other cdn as I am not sure will all records are affected or partial?
Daniel - 14/01/2019
Only those records marked in red in step 2 are affected, and it doesn’t matter what the DNS provider is, because any of them must allow you to specify a record name, a record type and a value and they work in a similar way.
Kyle - 22/01/2019
thanks for the write up! worked like a charm 🙂
cheolgyu - 05/03/2019
thank you 😀
Sala - 04/04/2019
Thank you, now I have a .4$ email for my domain! There were two thing that didn’t work straight away though:
1. Sending larger images(I tried over 3mb) failed. The issue was that the lambda function timeout was set to 3s. I guess it didn’t manage to send 3mb in 3s. I increased it to 3 min and now I can send larger images as well.
2. To move out of the smtp sandbox it was required to manage bounces and complaints with SNS. Just sending them to my gmail was sufficient. Here is a tutorial: https://medium.com/quick-code/how-to-handling-bounced-and-complaint-notification-in-aws-within-15-minutes-827972207484.
Robert Christian - 29/04/2019
Seems like any DNS service can be used, doesn’t have to be Route 53. I’m already on Cloudflare, so I just used that. Everything works.
Trying to figure out if it’s possible to do a rewrite of the raw text on all outgoing mail, to replace the From: address with the original sender’s address.
Robert Christian - 29/04/2019
I tried this and got it working, but I wasn’t happy with the From: address being rewritten to @example.com, so I found an alternative that’s actually easier. For all incoming mail, improvmx.com forwards to gmail or any other personal email address. For all outgoing mail, I use SES, like in the above setup.
Juliana - 14/06/2019
Hi you all,
I’m having this error in lambda function:
Response:
{
“errorType”: “Runtime.ImportModuleError”,
“errorMessage”: “Error: Cannot find module ‘streetdogspt-com-forwarding'”,
“trace”: [
“Runtime.ImportModuleError: Error: Cannot find module ‘streetdogspt-com-forwarding'”,
” at _loadUserApp (/var/runtime/UserFunction.js:100:13)”,
” at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)”,
” at Object. (/var/runtime/index.js:36:30)”,
” at Module._compile (internal/modules/cjs/loader.js:701:30)”,
” at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)”,
” at Module.load (internal/modules/cjs/loader.js:600:32)”,
” at tryModuleLoad (internal/modules/cjs/loader.js:539:12)”,
” at Function.Module._load (internal/modules/cjs/loader.js:531:3)”,
” at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)”,
” at startup (internal/bootstrap/node.js:283:19)”
]
}
I’m new at this, can you help me?
Thank you very much!
Sebastian - 27/08/2019
You saved me a whole lot of trouble with this guide!
There is very little about actual configuration of Lambda forwarder (fortunately this lambda got inline explanation in code) and it’s worth noting you should first create S3 bucket to then use as I think it wasn’t stated anywhere.
Nevertheless, awesome guide!
willie - 10/09/2019
Mr. Daniloaz,
First, thanks again for your post and replies to all these questions. I have tried to verify a second gmail account to send emails as an alias, but I am not able to. I know that the credentials are correct, because I established another alias on my original gmail, w*****.w*****@gmail.com, but I cannot set up another gmail to send under aliases with these same credentials. I am sure there is something I’m misunderstanding about the SMTP format.
Any explanation would really help.
Thanks,
Willie
Richard - 10/12/2019
Hi,
Awesome resource. If I may, you should just update the article with this emailKeyPrefix, I went through researching while it was in the comments.
Plus the Gmail screenshot in English and this piece will be a master class
Henry - 06/04/2020
Is there an easy way to filter given domains? I’ve been flooded with spam. I cannot use gmail spam filters since the sender is always myself.
Shinoj - 16/06/2020
Hi there,
I am using office 365 server as my SMTP, need to work with aws lambda. While triggering the function, i am getting error. Can I use office365 as SMTP in aws ~
Cael - 31/08/2021
Great guide!
Still working perfectly, considering you have to change the emailKeyPrefix param to “”, instead of commenting out the whole line.