Setting up Exim to Use Amazon Simple Email Service (SES)

Amazon Simple Email Service (SES) is a Email Marketer’s dream come true. It allows you to send bulk emails to customers without fear of having your email land in their spam/junk folder — unless of course you’re sending spam and a huge majority of your “customers” start marking it as such.

More to the point, it’s very easy to setup SES with sendmail and postfix. However, if you’re like most small/medium businesses, you’re using cPanel to host your site and that comes with Exim as the mail server. Of course, Amazon forgot to add instructions for Exim and so, here you are. Let’s begin.

First, I assume you’re using the standard cPanel Exim configuration which is a combined file. If you’re using a split config, you should take a look at this page (which is where I learned this stuff in the first place).

So, the first thing you want to do is sign up with AWS and then SES and get your AWS Access Key and Secret (try this link if you don’t know what that is). Once you have that, it’s time to download the basic official perl scripts that interact with SES.

cd /usr/local/
cd bin/
vi aws-credentials

Add the following two lines to it:


Then run:

./ -k aws-credentials -s
./ -k aws-credentials --verbose -v

Receive an email on the address you provided, click on the link. Access to that email inbox is all you need to verify. After that, you can use this email address to send emails from SES. This is important. When you first sign up, you are in “sandbox” mode. You can only send emails from verified emails and to verified emails. After you’re done testing, you can request production access and you will be able to send emails to unverified emails. From addresses always need to be verified — which is a good thing.

After the verification, you can send an email like so:

root@ec2-50-19-42-220:~/ses-script/bin# ./ -k /usr/local/bin/aws-credentials -s Test -f < test.txt
root@ec2-50-19-42-220:~/ses-script/bin# ./ -k /usr/local/bin/aws-credentials -s Test -f < test.txt
Email address is not verified.

Finally, copy the file to the Perl library and make the credential files readable.

cp /usr/local/lib/perl5/5.8.8/
chmod 644 /usr/local/bin/aws_credentials

Or wherever your Perl libraries are. So, that’s all good. Now that we have SES scripts setup, we need to tell Exim how to use it. Open up /etc/exim.conf (or whereever those config are) and add the following to the top:

AWS_SES_SEND_EMAIL = /usr/local/bin/
## File must be readable by the running exim group (e.g. Debian-exim)
AWS_CREDENTIALS_FILE = /usr/local/bin/aws-credentials
## the SES verified sender
AWS_SES_SENDER = lsearch*@;/usr/local/bin/ses_senders
## currently useless as there's only one endpoint offered. We're not using it. 

Define a router for the email using the following just after begin router construct.

begin routers

  debug_print = "R: aws_ses for $local_part@$domain"
  driver = accept
  senders = AWS_SES_SENDER
  transport = aws_ses_pipe

The most important line there is the senders command. Notice the variable use. We defined this variable above. Simply put all your verified emails line-separated in the file /usr/local/bin/ses_senders and make sure Exim can read it. It basically says that this router will be fired/picked if the sender is in the file pointed to by this variable. The effect of this is that you can put all your SES verified email addresses in the ses_senders file and the SES routing will be used only for those emails that come from these verified emails. All other emails will go through the normal route — which is good since Amazon will reject all emails coming from non-verified email addresses. If your usecase is such that you absolutely don’t want any email going out unless the email is verified, you can simply comment out the senders line. That way, all emails will go through SES and get rejected if they aren’t from verified addresses. Phew! That was long-winded.

Anyway, the final change is to add the following just after begin transports:

begin transports

  debug_print = "T: aws_ses_pipe for $local_part@$domain"
  driver = pipe
       "${if !eq{AWS_SES_ENDPOINT}{} {-e}}"\
       "${if !eq{AWS_SES_ENDPOINT}{} {AWS_SES_ENDPOINT}}" \
     -f $h_from: $local_part@$domain
  freeze_exec_fail = true
  message_prefix =
  return_fail_output = true

The important parameter to the command above is the -r. Well, they’re all important but this specific one tell the SES script that a raw email will be sent to it instead of just the message body. That’s what Exim sends and that’s what the script should expect. (By the way, if you get an error in your exim_mainlog saying Child process of aws_ses_pipe transport returned 1 from command: /usr/local/bin/, you might want to try getting rid of the two lines with the ${if...}. They mess up the parameters to the SES script in some cases.

Finally, to test it out, you can create a simple PHP script that sends emails:

$headers = 'From:';
mail('', 'Test email from SES', "Testbody \n Some more lines.", $headers);

And tail -f /var/log/exim_mainlog to see what’s going on with the email.


  1. I tried your method, and the method you linked to as original, in both cases, all the emails, even the internal emails get relayed through amazon SES, so if I send mails from to, the mails are relayed through amazon ses. Even the bounce emails get routed through ses. Any fix for that?

  2. recluze

    August 23, 2011 at 6:53 am

    Shahed, you want to add the verified emails to senders list and uncomment the “senders” line in the router. That way, only those emails coming from the verified emails will get routed through SES — all others go to virtualusers transport as is normal.

  3. Cannot parse credentials entry in . at line 56, line 3.
    I am using centOs Getting upper error when i try to run
    root@ns1 [/usr/local/bin]# ./ -k aws-credentials -s

  4. Hi, wonder what you’ve used for your amazon instance, is it heavy? do we really need to get heavy in amazon ec2? or light is just enough?

  5. I don’t see the benefit of this method over the documentation that Amazon provides directly on integrating Exim with SES:

    It appears that with your method, a new Perl interpreter may be launched, and a new https connection made for every message that is sent.

    With Amazon’s recommended approach, a persistent encrypted tunnel as use, as well as a standard SMTP interface, which could be held open to send multiple messages in some cases. Those details lead me to think that Amazon’s recommended approach would perform better.

    I would be curious see test results of performance testing between the two approaches.

  6. Hello recluze,

    I followed your steps and am running into a problem.

    All emails are still routed through our traditional SMTP server. I put the AWS route right underneath “begin routes” and have the ses_senders file created and chmodded to 0777.

    Has anyone else encountered this?

    Thanks in advance.

  7. recluze

    May 16, 2012 at 3:15 pm

    Thanks for the link. When I wrote the tutorial, there was no “official method” for Exim. Hence the tutorial. You should always follow the official instructions if available.

  8. Good read, and all this time I must say that the m fault I think you need to do. Me and my neighbor were preparing to do some research.

Leave a Reply

Your email address will not be published.


© 2017 recluze

Theme by Anders NorénUp ↑