Diagnosing and Fixing SES Deliverability Issues: Moving Away from Shared Email Addresses
What We Did
After receiving multiple SES (Simple Email Service) delivery warnings and bounce reports, we identified that the shared email address jada@queenofsandiego.com was being used across multiple legacy blast scripts and automated processes. We conducted a comprehensive audit of email sender configuration, DNS records, and SES account health, then implemented a strategy to transition away from the problematic sender address.
The Problem: SES "Nastigrams" and Reputation Damage
The core issue stemmed from how sender reputation works in AWS SES. When multiple unrelated processes and scripts use the same sender address, bounces, complaints, and delivery failures accumulate under a single reputation metric. Even if individual campaigns have good engagement, a single poorly-configured or legacy script can damage the reputation of that address across the entire account.
In this case, jada@queenofsandiego.com was being used by:
- Active blast campaigns via
/tools/send_blast.pyand/tools/run_scheduled_blast.py - Legacy LaunchAgent daemon process (
jada_blast.py) still active on the production Lightsail instance - Potentially outdated or improperly managed sender configurations in older campaign templates
This fragmentation meant we couldn't isolate which process was causing delivery issues, making it impossible to debug effectively.
Technical Audit Steps
SES Account Health Assessment
We checked the SES account status and sending health by reviewing:
- SES verified identities: confirmed all registered sender addresses and domain verification status
- SES configuration sets: reviewed settings for bounce, complaint, and delivery tracking
- SES sending limits: verified we hadn't exceeded account quotas
- DMARC forensic reporting: discovered the DMARC record was configured with
ruf=(reporting URI for forensic reports) andfo=1(forensic report on any alignment failure), which was generating excessive diagnostic emails
DNS and Domain Verification
We audited Route53 records for queenofsandiego.com to ensure proper email authentication:
- Checked SPF, DKIM, and DMARC TXT records in the Route53 hosted zone
- Verified SES domain verification tokens were properly configured
- Identified that DMARC forensic reports were cluttering the inbox with false-positive notifications
Key Route53 commands executed:
aws route53 list-hosted-zones
aws route53 list-resource-record-sets --hosted-zone-id <zone-id> \
--query 'ResourceRecordSets[?Type==`TXT`]'
Script and Process Inventory
We searched the codebase and production server for all references to jada@queenofsandiego.com:
- Checked
/tools/send_blast.pyand/tools/run_scheduled_blast.pyfor hardcoded sender addresses - Connected to the Lightsail instance via SSH and checked for active cron jobs using the address
- Identified the LaunchAgent plist file still configured to run the legacy
jada_blast.pydaemon - Searched the maintenance hub source for any embedded sender addresses in templates or access control configurations
Infrastructure Changes
DMARC Record Cleanup
We modified the DMARC TXT record for queenofsandiego.com to remove forensic reporting directives that were flooding the inbox with non-actionable diagnostic emails:
- Removed:
ruf=parameter (forensic report recipient) - Removed:
fo=1parameter (report on all alignment failures) - Kept:
rua=parameter for aggregate reporting (weekly digest of DMARC results)
This reduces noise while maintaining visibility into email authentication performance.
Staged Maintenance Hub Deployment
We deployed updated documentation to the maintenance hub staging environment:
- File:
/tools/maintenance/staging-index.html - Added: New access codes for First Mate / Travis role with appropriate scope limitations
- Documented: Transition strategy away from shared
jada@address in deployment notes
Key Decisions and Rationale
Why Separate Sender Addresses?
Using distinct sender addresses for different campaign types or processes provides several benefits:
- Reputation Isolation: A single poorly-performing campaign doesn't damage the sender reputation of all other campaigns
- Debugging: Easier to correlate SES bounce/complaint metrics with specific processes
- Segmentation: Allows for role-based access control—different teams can manage different sender addresses without full account access
- Compliance: Makes it easier to implement sender authentication and maintain audit trails per campaign type
Why Remove DMARC Forensic Reports?
Forensic reports generate a separate email for every single DMARC alignment failure, even when aggregate reporting already captures the data. This creates inbox clutter and makes it harder to identify genuine issues. Aggregate reports (weekly digests) provide sufficient visibility for most use cases.
Why Audit the LaunchAgent?
The legacy jada_blast.py daemon was still active on the production Lightsail instance despite newer blast scripts being in use. Inactive processes consume resources and create confusion about which code path is actually handling email sends. Identifying and deactivating it ensures we have a clear chain of custody for all outbound email.
Next Steps
- Email Sender Strategy: Define a new set of sender addresses for different campaign categories (e.g.,
campaigns@,transactional@,notifications@) and updatesend_blast.pyto use address-specific configuration - Verify SES Sending Health: Monitor SES metrics over the next 7 days to confirm bounce/complaint rates stabilize after DMARC record change
- Clean Up Legacy Scripts: Remove or disable
jada_blast.py` LaunchAgent from the production Lightsail instance to eliminate duplicate sending paths - Update Blast Pipeline: Ensure all active blast scripts reference the new, segmented sender addresses from configuration rather than hardcoded values
- Staging Deployment: Test the updated maintenance hub on staging, then promote to production once access code documentation is verified
Lessons Learned
Shared email addresses in automated systems create operational blind spots. By consolidating all sending through a single address, we lost the ability to distinguish between different processes' performance characteristics. Going forward, we'll treat sender addresses