
Every red team engagement that involves a phishing scenario runs into the same wall sooner or later: you need a mail server you fully control, one that behaves like a real corporate mail system, sends and receives reliably, and does not torch your reputation the moment you use it.
The answer is to compose your own infrastructure, on your own terms, with the same rigor you would expect from any production system.
This article walks through how we built exactly that: a self-hosted mail server on AWS, provisioned as code, designed specifically to support authorized red team activity and security awareness exercises.
It covers the architecture, the deployment process, the operational details that matter most for deliverability, and the practices we put in place to keep the whole thing manageable over time.
A red team mailbox is not just an inbox. It needs to send convincing mail, receive replies, pass basic authentication checks (SPF, DKIM, DMARC), and avoid landing in spam folders during a simulated campaign.
It also needs to be disposable and compartmentalized: one campaign’s domain reputation should never bleed into another’s, and a domain that gets burned during an exercise should be easy to retire without affecting the rest of the program.
Running the stack ourselves gives us three things a single managed service cannot:
Our mail infrastructure is not a single monolithic box, and that is by design. It is a hybrid stack made up of a handful of purpose-built pieces, each doing one job well, tied together by DNS and automation rather than by living on the same host or even the same provider.
At the core of our mail setup is Stalwart, an open source mail server that handles SMTP, IMAP, and JMAP, and ships with a web-based admin console and strong security defaults.
Stalwart runs as a Docker container on a dedicated EC2 instance, managed as a systemd service so it starts and stops cleanly with the machine.
Campaign execution, however, does not happen inside Stalwart itself. We run GoPhish (or propertary tools) on premises, and it connects out to the Stalwart mail server whenever a campaign needs to launch phishing simulations. This split matters for a few reasons. Keeping GoPhish on prem means campaign results never leave infrastructure we directly control. Keeping the mail-sending function on Stalwart in AWS means we get proper uptime, security groups and public exposure, without needing to bolt all of that onto an on-prem box that was never designed to be internet-facing mail infrastructure.
Each component does what it is good at, and the two only need to agree on SMTP credentials to work together.
Add Cloudflare for DNS and certificate automation, and SMTP2GO as the outbound relay, and the picture is clearly a diversified stack: different providers, different environments, on prem and cloud side by side.
What keeps that diversity from turning into chaos is that none of it is clicked together by hand.
As stated in other articles, we usually define and manage all our infrastructure as code, via tools like Terraform: that means the mail server is not something anyone clicks together in the AWS console, it is a repeatable, version-controlled deployment that can be destroyed and recreated on demand. The GoPhish side and the DNS/relay configuration follow the same principle, so the entire hybrid stack, cloud and on prem together, can be reasoned about, reviewed, and rebuilt from a single source of truth.
For a red team, this matters a lot: engagements are time-boxed, and there is real value in being able to bring up a fresh mail environment for a campaign and tear it down cleanly once the exercise is over, rather than leaving long-lived infrastructure sitting around as an unnecessary attack surface.
A typical deployment produces:
The overall architecture looks like this:

None of the infrastructure work matters if the mail ends up in spam or gets rejected outright.
DNS is where deliverability is won or lost, and it is worth treating as a first-class part of the build rather than an afterthought.
At minimum, a domain needs:
Using a DNS provider with a solid API, such as Cloudflare, makes this whole process far less painful as records can be pushed automatically during setup, which matters when you are standing up multiple domains for different engagements and do not want to hand-enter the same 20-ish records every time. A few records, notably the A and MX, still need to be added manually before the automated pieces can take over.
Here is a detail that catches a lot of people off guard: AWS (as virtually any cloud provider) throttles or blocks outbound traffic on port 25 (and other ports) by default, precisely to prevent its infrastructure from being used to send spam. That is a reasonable policy from AWS’s side, but it means a freshly deployed mail server will happily receive mail and will struggle to send any.
The practical fix is to route outbound mail through an SMTP relay service rather than sending directly from the EC2 instance. We evaluated AWS SES and found it more complex than necessary for this use case, and ended up going with SMTP2GO, which offers a straightforward free tier (200 emails a day, 1,000 a month, five domains) that covers most engagement needs.
Verifying the domain on SMTP2GO’s side is a one-time setup per domain while bringing Stalwart to route outbound traffic through the relay is single configuration after bootstrap: once that is in place, a restart of the Stalwart service picks up the new routing, and outbound mail starts flowing normally.
One principle we apply strictly: the primary domain used for the admin console and day-to-day mail server management is never used to send simulated phishing mail.
Every campaign uses a separate domain or subdomain, configured independently on Stalwart, with its own DNS zone, its own DKIM keys, and its own relay verification.
Domain and IP reputation are cumulative and sticky. A domain that gets reported as phishing during an authorized exercise, even a successful one, can end up on blocklists that are slow to clear. Keeping the “infrastructure” domain separate from the “campaign” domains means a burned domain from one exercise never puts the whole platform’s reputation, or the team’s ability to run the next engagement, at risk.
Adding a new domain follows a repeatable pattern: register the DNS zone (in our case through Cloudflare), point Stalwart at it with automatic certificate and DNS management via ACME and the Cloudflare API, create the mailbox users needed for the exercise, configure Gophish with that mailbox and verify the domain with the SMTP relay provider. Aliases and subdomains of an already-configured domain are even simpler, since the underlying mail routing and users are already in place.
Building your own red team mail infrastructure is less about mail server software and more about the surrounding discipline: DNS hygiene, domain segmentation, credential management, and a deployment process that is boring and repeatable enough that it never becomes a liability itself.
Stalwart handles the actual mail serving well, and Terraform makes the AWS and Cloudflare side reproducible, but the real value comes from treating this like the piece of security-critical infrastructure it is, with the same authorization boundaries, documentation, and operational care you would apply to any tool used in an engagement.
Used responsibly and within a properly authorized scope, this kind of setup gives a red team exactly what it needs: reliable, controllable mail infrastructure that supports realistic testing without putting anyone’s reputation, or anyone’s inbox, at unnecessary risk.