Setting up email sending by hand is fine the first time. You add a domain, copy the DKIM, SPF, and DMARC records into your DNS provider, wait for verification, create an API key, and paste it into a secret store. Then you do it again for staging. Then a teammate does it slightly differently for the EU environment, and six months later nobody remembers which webhook points where.
The SMTPfast Terraform provider turns that whole setup into code you can review, version, and re-apply. The headline: you can register a sending domain and publish its DNS records to Cloudflare or Route 53 in a single terraform apply.
What you can manage
The provider covers the pieces you would otherwise click through in the dashboard:
smtpfast_domain: a sending domain, with its DKIM, SPF, DMARC, and MAIL FROM records exposed as an output so you can publish them from the same config.smtpfast_api_key: an API key, optionally scoped. The secret is returned once at create time and treated as sensitive.smtpfast_webhook: a webhook endpoint and the events it subscribes to.- a
smtpfast_domaindata source for reading an existing domain's records.
It is built on the current Terraform plugin framework.
Getting started
Add the provider and give it an API key. The key can come from a variable or from the SMTPFAST_API_KEY environment variable, so nothing secret has to live in your config:
terraform {
required_providers {
smtpfast = {
source = "smtpfast/smtpfast"
}
}
}
provider "smtpfast" {
api_key = var.smtpfast_api_key
}
Run terraform init and Terraform pulls the provider from the registry. The source is open under MPL-2.0 if you want to read or contribute to it.
Register a domain and publish its DNS in one apply
This is the part that usually involves copy and paste. Declare the domain, then loop over the records it returns and hand them to your DNS provider:
resource "smtpfast_domain" "example" {
domain = "mail.example.com"
}
resource "cloudflare_record" "smtpfast" {
for_each = { for idx, rec in smtpfast_domain.example.dns_records : idx => rec }
zone_id = var.cloudflare_zone_id
type = each.value.type
name = each.value.name
content = each.value.value
proxied = false
}
output "domain_status" {
value = smtpfast_domain.example.status
}
Run terraform apply and the domain is created in SMTPfast, the DKIM, SPF, DMARC, and MAIL FROM records land in Cloudflare, and verification starts automatically. Swap cloudflare_record for aws_route53_record or your own DNS provider and the shape stays the same. No copying record values between two browser tabs, no typos in a long DKIM key.
API keys as code
Keys are a resource too. Scope them to what a workload actually needs, and remember that the secret is only available on create, so pipe it straight into wherever it belongs:
# A scoped key that can only send email.
resource "smtpfast_api_key" "send_only" {
name = "app-send-only"
scopes = ["emails:send"]
}
output "app_api_key" {
value = smtpfast_api_key.send_only.key
sensitive = true
}
Because the key lives in state, treat your state the way you already treat secrets: a remote backend with encryption at rest, not a file in the repo. Rotating a key becomes a one-line change and an apply, and the old key is cleaned up when you remove the resource.
Webhooks as code
Point events at your handler and keep the subscription list in version control, where a reviewer can see when someone adds email.complained to the list:
resource "smtpfast_webhook" "events" {
url = "https://example.com/webhooks/smtpfast"
events = [
"email.delivered",
"email.bounced",
"email.complained",
]
}
Reading an existing domain
If a domain already exists, the data source reads its records so you can reference them elsewhere, for example to reconcile DNS that was set up before you adopted Terraform:
data "smtpfast_domain" "example" {
id = "dom_xyz789"
}
output "domain_dns_records" {
value = data.smtpfast_domain.example.dns_records
}
Why this matters
The real win is not saving a few clicks. It is that your email setup becomes reproducible and reviewable:
- Staging and production match. The same module builds both, so a domain that verifies in staging verifies in production.
- Changes go through review. Adding a webhook event or rotating a key is a diff, not a dashboard action nobody logged.
- Recovery is boring. If an environment is lost,
terraform applyrebuilds the domains, keys, and webhooks from the config.
If you already manage your infrastructure with Terraform, your sending setup can live right next to it. Point the provider at your account, describe the domains and keys you want, and let one apply do the rest.
Grab the provider on GitHub, and if you do not have an account yet, create one free and send your first email in a few minutes.
Ready to get started?
Start sending transactional email today. Free to start, no credit card required.
Get Started for FreeRelated Posts
The Best Free SMTP Services for Developers in 2026
Ten free SMTP services compared for apps, CMSs and devices: free-tier limits, daily caps, ports and TLS. Every number checked on the provider's own pages.
Best Transactional Email Services for SaaS in 2026
SMTPfast, Mailtrap, Postmark, Twilio SendGrid and Amazon SES compared for SaaS products: free tiers, real prices, inbound email, and the tradeoffs each one makes.