Email from Rails with SES on EC2

Amazon Simple Email Service (SES) is a high availability transactional email service. It is very inexpensive to send email with SES from an EC2 instance:

It is available in three AWS regions:

Configure AWS

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

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.

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.

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.

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

Install the official aws-sdk-rails gem. It uses other aws-sdk-ruby gems aws-sdk-ses and aws-sdk-core and adds an ActionMailer delivery method.

# Gemfile
gem 'aws-sdk-rails'

# config/environments/production.rb
Aws::Rails.add_action_mailer_delivery_method(:aws_sdk, region: 'us-west-2')
config.action_mailer.delivery_method = :aws_sdk

# app/controllers/users_controller.rb
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

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