Send BugSnag On-premise email through Amazon SES using an IAM role, with no stored email credentials.
This guide sets up BugSnag to send its emails (alerts, invites, password resets) through Amazon SES using an IAM role, so no email username or password is stored anywhere. It takes about 30 to 45 minutes of work, plus waiting time while AWS verifies your domain and, for new SES accounts, reviews your request for production access.
sidekiq, which runs in your Kubernetes cluster.sidekiq an AWS identity: Role A, the pod identity role.You enter Role B’s ARN in the BugSnag settings. BugSnag does the rest.
sidekiq (BugSnag's email program, in your cluster)
│ EKS Pod Identity gives it Role A (steps 3, 4, 7)
▼
Role A: BugSnagSidekiq — pod identity role, can only switch to Role B
│ switches to Role B (steps 5, 6)
▼
Role B: BugSnagSESSender — SES sending role; its ARN is entered in BugSnag (step 8)
│ sends the email from your verified domain (step 1)
▼
Amazon SES — delivers the email to your team's inboxes
| Term | What it means |
|---|---|
| IAM role | A set of AWS permissions that something (here, BugSnag) can use. It has no password. |
| Trust policy | The part of a role that says who may use the role. |
| Permissions policy | The part of a role that says what the role may do. |
| ARN | The unique ID of an AWS resource, in the form arn:aws:iam::<ACCOUNT_ID>:role/BugSnagSESSender. |
| EKS Pod Identity | The EKS feature that hands an IAM role to a program running in the cluster. |
| Service account | The name a program runs under inside Kubernetes. BugSnag’s email program uses sidekiq. |
| SES identity | A domain or email address you have proven to AWS that you own, so SES will send from it. |
| SES sandbox | The restricted mode every new SES account starts in. It only delivers to verified addresses. |
| Namespace | The area of the cluster BugSnag is installed into. |
Collect these six values first. Throughout this guide, replace each placeholder with your value.
| Value | Placeholder | Where to find it |
|---|---|---|
| AWS account ID | <ACCOUNT_ID> |
AWS Console: click your account name (top right) and copy the 12-digit Account ID. |
| SES region | <REGION> |
Usually the region your EKS cluster runs in, shown top right in the AWS Console. If you already send email with SES in another region where your account has production access, you can use that region. |
| Cluster name | <CLUSTER_NAME> |
AWS Console → Elastic Kubernetes Service → Clusters. |
| BugSnag namespace | <NAMESPACE> |
BugSnag admin console → Config → Namespace. For a new installation, the namespace you will choose during install. |
| Sending domain | <DOMAIN> |
The domain your BugSnag emails will come from. |
| From address | <FROM_ADDRESS> |
The address BugSnag emails are sent from, on the sending domain. |
AWS permissions you need: a user that can manage IAM roles, EKS clusters and SES. An AWS administrator has all of these.
This guide assumes your EKS cluster and your SES sending domain are in the same AWS account. If they are not, see Using an existing SES account.
Wait until your domain shows Verified (step 1) before you install or switch over. Otherwise the first emails, such as the admin invite, fail and are retried until SES accepts them.
This proves to AWS that you own the domain, so SES will send email from it. You need a verified sending domain even if your SES account already has production access.
<REGION> (top right).<DOMAIN>.Warning: use your own domain with DKIM. Many organizations publish a DMARC policy that tells mail servers to reject email that is not properly signed for their domain. SES still accepts these messages, so the only sign of a problem is a bounce from MAILER-DAEMON@amazonses.com. Sending from a domain verified with Easy DKIM in this step avoids this. Do not use an address on a domain you have not verified here.
New SES accounts start in a sandbox. In the sandbox, SES only delivers to addresses you have verified one by one, and sends at most 200 emails in 24 hours. Production access is granted separately for each region.
Check your account in <REGION>: in SES, open Account dashboard from the left menu.
You can carry on with the next steps while you wait.
Note: testing before production access. While the account is in the sandbox, you can still test by verifying the addresses you will send to. In Configuration → Identities, click Create identity, choose Email address and enter the recipient’s address. The recipient must click the link in the verification email from AWS, and the address must also be added to Role B’s permissions policy (see step 5). Emails to anyone else fail with Email address is not verified until production access is granted.
EKS Pod Identity needs a small helper, the Pod Identity Agent, installed in the cluster.
<CLUSTER_NAME>.This is the role BugSnag’s sidekiq program receives. Create it first, because Role B has to name it.
BugSnagSidekiq.Expected trust policy for Role A:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "pods.eks.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession"]
}]
}
Role A gets its one permission in step 6, once Role B exists.
This role is allowed to send email. BugSnag switches to it from Role A.
<ACCOUNT_ID>.BugSnagSESSender and click Create role.BugSnagSESSender role. Click Add permissions → Create inline policy, then switch to the JSON tab.<REGION>, <ACCOUNT_ID> and <DOMAIN>.SendEmailViaSES and click Create policy.arn:aws:iam::<ACCOUNT_ID>:role/BugSnagSESSender). You will enter it in BugSnag in step 8.Trust policy for Role B. It allows only Role A to switch to this role:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::<ACCOUNT_ID>:role/BugSnagSidekiq" },
"Action": ["sts:AssumeRole", "sts:TagSession"]
}]
}
Permissions policy for Role B. It allows sending only from your verified domain. BugSnag needs both actions:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["ses:SendEmail", "ses:SendRawEmail"],
"Resource": ["arn:aws:ses:<REGION>:<ACCOUNT_ID>:identity/<DOMAIN>"]
}]
}
If you plan to use an SES configuration set (optional, step 8), add its ARN to the Resource list: "arn:aws:ses:<REGION>:<ACCOUNT_ID>:configuration-set/<CONFIGURATION_SET>".
Warning: while your account is in the sandbox. In the sandbox, SES also checks Role B’s permission against each recipient, because recipients are verified identities too. Add the ARN of every address you verified for testing to the Resource list: "arn:aws:ses:<REGION>:<ACCOUNT_ID>:identity/<RECIPIENT_ADDRESS>". Otherwise every email fails with AccessDenied naming the recipient. You can remove these entries once production access is granted.
BugSnagSidekiq (Role A).<ACCOUNT_ID>.AssumeBugSnagSESSender and click Create policy.Permissions policy for Role A:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Resource": "arn:aws:iam::<ACCOUNT_ID>:role/BugSnagSESSender"
}]
}
This step tells EKS: whenever the sidekiq program runs in the BugSnag namespace, give it Role A.
<CLUSTER_NAME>.BugSnagSidekiq.<NAMESPACE>.sidekiq, typed exactly like this, in lowercase.This works even if BugSnag is not installed yet. EKS applies the role to any program that later starts as sidekiq in that namespace. The namespace must match the one BugSnag is, or will be, installed into.
Open the BugSnag admin console and go to Config → Email. For a new installation, this is on the config screen during install. Fill in the settings below, click Save config, then Deploy.
| Setting | What to enter |
|---|---|
| Send email from | <FROM_ADDRESS>. It must be on the domain verified in step 1. |
| Email delivery method | Amazon SES (IAM role). This option only appears when the cloud provider is set to AWS. |
| Amazon SES region | <REGION>. It must be the region where your domain is verified and, for production use, where your account has production access. |
| Amazon SES IAM role ARN | The Role B ARN you copied in step 5. |
| Amazon SES configuration set | Leave blank, unless you have set one up in SES. |
Also check that Config → Service access → Dashboard starts with https://. BugSnag uses this address for the links in its emails.
Existing installations only: a program only picks up its AWS identity when it starts. If BugSnag was already running before step 7, restart the email program:
kubectl rollout restart deployment/sidekiq -n <NAMESPACE>
kubectl exec -n <NAMESPACE> deploy/sidekiq -c sidekiq -- printenv AWS_CONTAINER_CREDENTIALS_FULL_URI
kubectl logs -n <NAMESPACE> deploy/sidekiq -c sidekiq | grep -i -E "ses|sts|mail|denied"
BugSnag retries emails that fail. Once you fix the cause, waiting emails are sent automatically, so there is no need to invite users again.
| What you see | What it means | How to fix it |
|---|---|---|
The printenv command prints nothing |
The email program has no AWS identity. | Check the Pod Identity Agent is Active (step 3). Check the namespace and service account in step 7 are exactly right. Then restart sidekiq (step 8). |
AccessDenied mentioning sts:AssumeRole |
Role A cannot switch to Role B. | Check Role B’s trust policy names BugSnagSidekiq (step 5) and Role A’s policy names BugSnagSESSender (step 6). Check the account ID for typos. |
AccessDenied mentioning ses:SendEmail or ses:SendRawEmail |
Role B may not send from that address. | Check Role B’s permissions policy has both actions, and that its region, account ID and domain are right (step 5). Check Send email from is on that domain. If you use a configuration set, check it is in the policy too. If the error names the recipient’s address and your account is in the sandbox, add that recipient’s identity to the policy (step 5). |
Email address is not verified |
SES does not trust the from address, or the account is still in the sandbox and the recipient is not verified. | Check the domain shows Verified in the same region (step 1) and production access was granted in that region (step 2). To test in the sandbox, verify the recipient’s address. |
No error in the logs, but a bounce arrives from MAILER-DAEMON@amazonses.com |
SES sent the email but the recipient’s mail server rejected it, usually because of the sending domain’s DMARC policy. | Send from an address on a domain verified with Easy DKIM in step 1, and check all three DKIM records are in DNS. |
| Emails arrive in spam | The domain’s email authentication is incomplete. | Make sure all three DKIM records are in DNS (step 1). Consider adding SPF and DMARC records for your domain. |
Links in emails start with http:// |
The Dashboard address has no protocol. | Set Dashboard to an address starting with https:// (step 8). |
If your organization already sends email with SES, you can reuse that account. If it has production access in your SES region and your domain is verified with DKIM there, you only need steps 3 to 9. Keep in mind:
For administrators who prefer the command line, this covers steps 3 to 7. Replace the placeholders in the first five lines with your values, then run the rest. Steps 1 and 2 involve DNS changes and an AWS review, so do them in the console.
ACCOUNT_ID="<ACCOUNT_ID>"
REGION="<REGION>"
CLUSTER="<CLUSTER_NAME>"
NAMESPACE="<NAMESPACE>"
DOMAIN="<DOMAIN>"
# Step 3: Pod Identity Agent
aws eks create-addon --cluster-name $CLUSTER --addon-name eks-pod-identity-agent --region $REGION
# Step 4: Role A (pod identity role)
aws iam create-role --role-name BugSnagSidekiq --assume-role-policy-document '{
"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Principal":{"Service":"pods.eks.amazonaws.com"},
"Action":["sts:AssumeRole","sts:TagSession"]}]}'
# Step 5: Role B (SES sending role)
aws iam create-role --role-name BugSnagSESSender --assume-role-policy-document '{
"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::'$ACCOUNT_ID':role/BugSnagSidekiq"},
"Action":["sts:AssumeRole","sts:TagSession"]}]}'
aws iam put-role-policy --role-name BugSnagSESSender --policy-name SendEmailViaSES --policy-document '{
"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Action":["ses:SendEmail","ses:SendRawEmail"],
"Resource":["arn:aws:ses:'$REGION':'$ACCOUNT_ID':identity/'$DOMAIN'"]}]}'
# Step 6: allow Role A to switch to Role B
aws iam put-role-policy --role-name BugSnagSidekiq --policy-name AssumeBugSnagSESSender --policy-document '{
"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Action":["sts:AssumeRole","sts:TagSession"],
"Resource":"arn:aws:iam::'$ACCOUNT_ID':role/BugSnagSESSender"}]}'
# Step 7: attach Role A to the sidekiq service account
aws eks create-pod-identity-association --region $REGION \
--cluster-name $CLUSTER --namespace $NAMESPACE --service-account sidekiq \
--role-arn arn:aws:iam::$ACCOUNT_ID:role/BugSnagSidekiq
# The ARN to enter in BugSnag (step 8)
echo arn:aws:iam::$ACCOUNT_ID:role/BugSnagSESSender