Email with Amazon SES from Rails on EC2

Amazon Simple Email Service (SES) is a high availability transactional email service.

When sending email from an EC2 instance, it is particularly inexpensive:

It is available in three AWS regions:

If you have an AWS account, go to SES in your AWS console.

Move out of sandbox mode

By default, SES in each region is in sandbox mode. Since sandbox mode only allows delivering to a whitelist of verified email addresses, it's unusable for a production web application.

To move out of sandbox mode, go to the "Sending Statistics" page and click "Request a Sending Limit Increase" to open a support request. It may take up to a day or so to be approved.

Once improved, the sending limit will increase from 200 emails/day to 50,000 emails/day and the restriction on delivering only to verified email addresses will be lifted.

Verify an email address

Click "Email Addresses". Click "Verify a New Email Address". Enter an email such as support@example.com. Click the verification link that is emailed to the address.

This address is now allowed to deliver emails using SES.

EC2 instance profile credentials

If the Rails app is deployed to EC2, the aws-sdk-* gems will load credentials from the EC2 instance's metadata. The IAM role associated with the EC2 instance will be found.

IAM role and policy

Go to Policies in the IAM console. Click "Create policy". Choose "SES". Expand the "Write" section. Select "SendEmail" and "SendRawEmail". Create the policy.

Go to Roles in the IAM console. Find the role associated with the EC2 instance. Attach the policy.

Configure Rails

Add the official aws-sdk-rails gem to the Gemfile in the Rails app:

gem 'aws-sdk-rails'

It lightly wraps other aws-sdk-ruby gems such as aws-sdk-ses and aws-sdk-core. It provides an ActionMailer delivery method.

Configure your chosen AWS region in config/initializers/simple_email_service.rb:

Aws::Rails.add_action_mailer_delivery_method(:aws_sdk, region: 'us-west-2')

Use the delivery method in config/environments/production.rb:

config.action_mailer.delivery_method = :aws_sdk

Once your sending limit has been increased, you can deploy these changes.

Catch errors

When delivering emails, optionally rescue Aws::SES::Errors::ServiceError errors:

class UsersController < ApplicationController
  def create
    @user = User.new(email: params[:user][:email])

    if @user.save
      begin
        Mailer.email_confirmation(@user).deliver_now
      rescue Aws::SES::Errors::ServiceError => err
        Rails.logger.info(err.message)
        flash.now[:alert] = t('.failure')
        render :new
      end
    else
      flash.now[:alert] = t('.failure')
      render :new
    end
  end
end