Daniel López Azaña

Theme

Social Media

Blog

GNU/Linux, Open Source, Cloud Computing, DevOps and more...

Use GMail with your own domain for free thanks to Amazon SES & Lambda

GMail account configured to send to and receive email from our own domain

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.

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.

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: <a href="mailto:myexample@gmail.com" rel="noopener noreferrer" target="_blank">myexample@gmail.com</a>

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:

AWS Email GMail Lambda Route 53 SES SMTP
Daniel López Azaña

About the author

Daniel López Azaña

Tech entrepreneur and cloud architect with over 20 years of experience transforming infrastructures and automating processes.

Specialist in AI/LLM integration, Rust and Python development, and AWS & GCP architecture. Restless mind, idea generator, and passionate about technological innovation and AI.

Related articles

AWS security groups

How to automatically update all your AWS EC2 security groups when your dynamic IP changes

One of the biggest annoyances when working with AWS and your Internet connection has a dynamic IP is that when it changes, you immediately stop accessing to all servers and services protected by an EC2 security group whose rules only allow traffic to certain specific IP’s instead of allowing open connections to everyone (0.0.0.0.0/0).Certainly the simplest thing to do is always allowing traffic on a given port to everyone, so that even if you have a dynamic IP on your Internet connection you will always be able to continue accessing even if it changes. But opening traffic on a port to everyone is not the right way to proceed from a security point of view, because then any attacker will be able to access that port without restrictions, and that is not what you want.

January 12, 2021
terraform-and-route53-logos

How to quickly import all records from a Route53 DNS zone into Terraform

The terraform import command allows you to import into HashiCorp Terraform resources that already existed previously in the provider we are working with, in this case AWS. However, it only allows you to import those records one by one, with one run of terraform import at a time. This, apart from being extremely tedious, in some situations becomes impractical. This is the case for the records of a Route53 DNS zone. The task can become unmanageable if we have multiple DNS zones, each one with tens or hundreds of records. In this article I offer you a bash script that will allow you to import in Terraform all the records of a Route53 DNS zone in a matter of seconds or a few minutes.

February 8, 2022
Copy AMI using customer managed key for encryption

How to share an AMI between 2 AWS accounts

If you have an unencrypted AMI you can share it with another AWS account directly without doing anything special. But if the AMI is encrypted, things get complicated, as the destination account won't have the encryption key to decrypt its snapshots and you won't be able to share it. In this article I'll show you how to properly share an encrypted AMI between two AWS accounts using customer managed KMS keys, allowing you to securely share your EC2 instance images across different AWS accounts.

February 6, 2022

Comments

Carlos Kynäslahti October 8, 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 October 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 October 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 October 4, 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 October 11, 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 October 12, 2017
Solving problems on your own is the best way to learn :) Thank you for taking the time to write this guide!
Dan Rose December 6, 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 December 19, 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 January 13, 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?
Suyash April 16, 2018
Hi, Outstanding post. Better than all those docs on aws.amazon.com
Johnny July 15, 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 August 27, 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 August 27, 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 January 5, 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 September 4, 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 September 6, 2018
Amazing post! Working like charm the very first time.
Michael OConnor September 11, 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 September 21, 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 June 14, 2019
The same happen with me :(
Alberto August 12, 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 November 19, 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 January 8, 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 January 8, 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 January 10, 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 January 14, 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 January 22, 2019
thanks for the write up! worked like a charm :)
cheolgyu March 5, 2019
thank you :D
Sala April 4, 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 April 29, 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 April 29, 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 June 14, 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 August 27, 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 September 10, 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 December 10, 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 April 6, 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 June 16, 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 August 31, 2021
Great guide! Still working perfectly, considering you have to change the emailKeyPrefix param to "", instead of commenting out the whole line.

Submit comment