SES on AWS: Getting Started with Email on AWS

I am currently working on a WordPress hosting platform for my friends and family. One of the first things that comes up when configuring WordPress is email access. WordPress constantly wants to send emails for verification and other purposes, and for most people email and domains are hard to configure. So, I’ve created a system to automatically setup a domain, and audit it for misuse. This series will be focus on creating a domain and associated email (using SES) from scratch. It will be divided into 3 sections:

  1. Creating Route 53 and SES resources to allow us to send emails from a domain.
  2. Setting up our auditing our mechanisms so that our friends, family, or customers cannot abuse our service.
  3. Finalising our email policy, removing SES account restrictions, and testing.

I want to make sure the solution is easy to use and easy to reuse, so all of the examples will be provided as terraform scripts. As I want this to be runnable as a batch process, I will be building a terraform docker container that will run the full setup. While containers are useful, I will also be making the code fully available at my GitHub repository.

SES Architecture

Our architecture is quite simple: a mix of SES and Route 53 to provide the actual email functionality, and SNS, SQS, Lambda, and S3 to provide the auditing functionality. All the components and their purpose are provided in the diagram below.

SES on AWS Component Overview
Component Overview

Logical Flow

The solution while complicated-looking is quite straightforward. The basic flow that is followed when the solution is in use is:

  1. An email is sent
  2. SES checks to see if the IAM user that is sending the email has authorisation for that domain
  3. The email is processed via SES and delivery is attempted

This occurs every time an email is sent through SES. If email delivery is successful, nothing further occurs. However, if delivery fails then we follow the complaints and bounce process. This is as follows:

  1. A complaint or bounce back is received by SES
  2. SES sends a message to SNS
  3. SNS send the messages to SQS
  4. Once a day, a Lambda function processes all the pending messages
  5. The messages are stored in an S3 bucket for later processing

What is a failure, really?

Now that we have the failures, it is important to assess them against a set of rules. Now, we don’t want to consider every message we receive a hard strike against the person who sent an email. To provide context let us understand what the types of messages we may receive back from SES when a message fails to deliver:

  • Bounce: A bounce is when a email cannot be delivered to a user
    • Soft Bounce: Occurs when a user exists, but mail is undeliverable (e.g. when a mailbox is full)
    • Hard Bounce: When the user or domain does not exist
  • Complaint: When a user clicks the “spam” button on an email, a notification is sent back to the source of the message
  • Virus: When SES detects a virus in the message it will reject the message.

Now that we know what kind of failures we can expect from SES, let’s come up with some rules on how to handle them.

Soft Bounce

The action is not malicious on the part of your customer. Delivery of further messages should be allowed, however after a limit we should block further attempts and notify the customer.

Hard Bounce

These are not generally considered malicious when they are received in small amounts. Delivery of further messages to the faulty email address should not be allowed and the customer notified so they can update their contact.

However, there are cases in which a hard bounce can actually be malicious. If you see a large amount of hard bounces from a single email address, it is likely that your customer is attempting to guess the email addresses. This information can be scraped from Linkedin (for example) by getting their full name and the domain name of the company that the person works for. Most email addresses follow the format so it is quite easy to guess. If you see a large amount of hard bounces, delivery should be restricted and information about the customer’s emailing practices should be requested before further sending is allowed.


Some people don’t like any of kind of spammy-looking email, this is a fact of life. Because of his, we can’t consider a single complaint to be legitimate. In fact, we can’t even consider three complaints legitimate: these just may be a user who wants to unsubscribe from a mailing list. So, we take a two step approach with how we handle complaints:

  1. If a few complaints come in for a user, block sending to that user and tell the customer they should remove that user from their mailing list
  2. If a lot of complaints come in from multiple users, block sending from the domain unless the customer can explain their behaviour. When they are found to be spammy in nature, their access to your email service should be terminated, lest AWS terminates your own access to SES.

Taking a measured approach towards complaints means that we can generally keep everyone happy. We aren’t so ridiculous with our approach that an mis-click can cause someone grief, but if it’s obvious someone does not want to be receiving email from someone then we can take appropriate action.

The feedback loop used by this email system is very important, and, managing complaints correctly and promptly is the best way to maintain your account reputation.


If a customer send a virus using your service, they get one more chance before their access is terminated. Viruses are not to be tolerated, regardless of whether they are sent through negligence (being infected) or malfeasance (deliberately sending of a virus).

Closing the Loop: Actioning SES Reports

Now that we have the information from the reports, and a way to deal with this information, it is time to actually do something with it. The final step in the automated process is communicating to the customer, and (if applicable) taking action against their account. This is done by a Lambda function that runs once a week. The reports will be actioned as follows:

  • Bounce
    • Soft bounce
      • Reattempt delivery next time a message is sent
      • Once too many attempts are made, notify the customer and block sending to the address
    • Hard bounce
      • Block sending to the address immediately and notify the customer
      • If a large volume of bard bounces occur, disable sending and notify the customer
      • Terminate access to the service if the customer is attempting to guess email addresses
  • Complaint
    • If there have been 1-2 complaints, continue to allow sending
    • If there have been 3 complaints, prevent further sending and notify the customer that they can no longer email that person
    • Terminate access to the service if there is a large volume of complaints from the customer
  • Virus
    • If a single virus is received, disable sending from the domain and notify the customer that sending will only be enabled once they can explain the virus
    • Terminate access to the service if a second virus is received

Once the report is actioned, we will then use the outcomes to start to generate a behaviour report on our customers. This will allow us to inform action we take against them and ensure our reputation is not impacted by poor customer behaviour.

Whats next?

In the next article we’ll be tackling building the basic framework in our account to send emails via SES. This will provide a Terraform script (and associated container) that will setup a domain for email, verify it with SES, and setup an IAM user with appropriate permissions.

See you soon!

Leave a Reply

Your email address will not be published. Required fields are marked *