26 Sep 2026
Django community aggregator: Community blog posts
Setting Up DNS for SaaS Emails

When you create a Software as a Service (SaaS) or a social web platform, one of the often-overlooked parts of it is the email DNS configuration. I learned this too late with my own projects, and as a result, many of my initial emails landed in spam folders. Here are my learnings about it from 5 years of running a SaaS business.
Consider using separate subdomains for different types of email
The emails you send generally fall into these categories:
- direct - emails that you send manually.
- transactional - emails that your website sends for signup confirmations, two-factor authentications, password resets, etc.
- marketing - onboarding emails, mailing-list newsletters, birthday greetings, etc.
Marketing emails are frequent and not always wanted, so recipients may mark them as spam, which can hurt your sender reputation. Deliverability also depends on factors such as authentication, engagement, list quality, and sending practices (You can check your existing email spamminess at SpamHaus or MxToolbox).
For this reason, it can be useful to separate marketing emails from direct and transactional emails using different subdomains.
If example.com is your marketing website, and app.example.com is your SaaS, your main emails could be:
info@hello.example.com- for marketing / newslettershello@mail.example.com- for transactional and direct emails
MX records for receiving emails
An MX record (Mail eXchange record) is a type of DNS record that specifies which mail server is responsible for accepting email on behalf of a domain. When somebody sends an email to you, the sending mail server looks up the MX record for your domain to find out where to deliver that message.
- In the host field you would set your subdomain (or
@for the root domain). - The values contain the domain of the mail server and the priority of that server (a lower preference number means higher priority). If the server with higher priority is unreachable, the mail will fall back to the secondary one.
- TTL (Time To Live) controls how long DNS resolvers are allowed to cache a record before checking back for updates. While configuring and testing the server you can set it to a low value like
5 min, but when everything is working reliably, you can switch toAutomatic.
Here is an example setup pointing to FastMail servers.
| Type | Host | Value | TTL |
|---|---|---|---|
| MX Record | hello | in1-smtp.messagingengine.com. 10 | Automatic |
| MX Record | hello | in2-smtp.messagingengine.com. 20 | Automatic |
| MX Record | in1-smtp.messagingengine.com. 10 | Automatic | |
| MX Record | in2-smtp.messagingengine.com. 20 | Automatic |
Note that Fastmail does not allow its service to be used for automated or transactional emails, but you might need these settings for receiving your direct emails and replies.
SMTP setup for outgoing emails
SMTP (Simple Mail Transfer Protocol) is the protocol used to transfer email between mail servers and from mail clients or applications to mail servers. SMTP will be used no matter whether you send a direct email from an email client like FastMail, or a transactional email from Amazon SES, Postmark, Brevo, Mailjet, Resend, SendGrid, Mailgun, or Postal.
SPF records for outgoing emails: Which senders are allowed?
An SPF record (Sender Policy Framework) is a type of DNS TXT record that specifies which mail servers are allowed to send email on behalf of your domain. It's one of the core mechanisms used to prevent email spoofing. SPF has one record per domain or subdomain and multiple senders must be combined into one line.
| Type | Host | Value | TTL |
|---|---|---|---|
| TXT Record | hello | v=spf1 include:spf.messagingengine.com include:spf.mailjet.com -all |
Automatic |
| TXT Record | v=spf1 include:spf.messagingengine.com include:spf.mailjet.com -all |
Automatic |
Here:
v=spf1- declares this is an SPF record, version 1.include:domain.com- delegate to another domain's SPF record.-all- hard fail - SPF fails for unauthorized senders.~all- soft fail - SPF fails, but the receiver may still accept the message.?all- neutral - SPF makes no policy statement.
Some of the well known includes:
include:spf.messagingengine.com- FastMailinclude:_spf.google.com- Google Workspaceinclude:spf.mailjet.com- Mailjetinclude:spf.brevo.com- Brevoinclude:servers.mcsv.net- MailChimpinclude:amazonses.com- Amazon SES or Resend (which is built on top of Amazon SES)include:sendgrid.net- SendGridinclude:mailgun.org- Mailgun
DKIM records for outgoing emails: Is the sender valid?
A DKIM record (DomainKeys Identified Mail) is a DNS TXT record containing a public cryptographic key that lets receiving mail servers verify that an email genuinely came from your domain and wasn't altered in transit. Sometimes, it's a CNAME record pointing to the servers of the mail service, which contains the DNS TXT records with the cryptographic keys. Typically, the Host is <prefix>._domainkey.<your_subdomain>.
Here is an example Mailjet config:
| Type | Host | Value | TTL |
|---|---|---|---|
| TXT Record | mailjet._domainkey.hello | k=rsa; p=<unique_id> |
Automatic |
| TXT Record | mailjet._domainkey.mail | k=rsa; p=<unique_id> |
Automatic |
For FastMail, it would be:
| Type | Host | Value | TTL |
|---|---|---|---|
| CNAME Record | fm1._domainkey.hello | fm1.hello.<root_domain>.dkim.fmhosted.com. |
Automatic |
| CNAME Record | fm1._domainkey.mail | fm1.mail.<root_domain>.dkim.fmhosted.com. |
Automatic |
| CNAME Record | fm2._domainkey.hello | fm2.hello.<root_domain>.dkim.fmhosted.com. |
Automatic |
| CNAME Record | fm2._domainkey.mail | fm2.mail.<root_domain>.dkim.fmhosted.com. |
Automatic |
| CNAME Record | fm3._domainkey.hello | fm3.hello.<root_domain>.dkim.fmhosted.com. |
Automatic |
| CNAME Record | fm3._domainkey.mail | fm3.mail.<root_domain>.dkim.fmhosted.com. |
Automatic |
DMARC records for outgoing emails: What to do with unauthorized senders?
DMARC (Domain-based Message Authentication, Reporting & Conformance) is a DNS TXT record that ties SPF and DKIM together into a single policy, telling receiving mail servers what to do when a message fails authentication, and optionally sending you reports about it.
| Type | Host | Value | TTL |
|---|---|---|---|
| TXT Record | _dmarc.mail | v=DMARC1; p=reject; pct=100; rua=mailto:dmarc-reports@mail.<root_domain> |
Automatic |
| TXT Record | _dmarc.hello | v=DMARC1; p=reject; pct=100; rua=mailto:dmarc-reports@mail.<root_domain> |
Automatic |
Here:
v=DMARC1- Version - always this exact string.pstands for Policy - what receivers should do on failure:none(monitor only, no action),quarantine(send to spam),reject(block outright).pct=is the percentage of messages to which the policy should be applied.rua=mailto:...- Where to send aggregate reports as daily XML summaries of pass/fail volumes.
The daily aggregate reports are a little annoying, but they are required for the emails to have little probability to land in a Spam directory.
Brevo setup as an exception
Brevo requires its own set of CNAME and TXT records to authenticate your domain and improve email deliverability. You would get the setup instructions while onboarding.
BIMI records for the outgoing emails: What brand logo will be shown?
BIMI (Brand Indicators for Message Identification) is a DNS TXT record that lets your logo appear as the sender avatar next to your emails in supporting email clients and services such as Gmail, Yahoo Mail, Apple Mail, and Fastmail.
Here is an example of BIMI records:
| Type | Host | Value | TTL |
|---|---|---|---|
| TXT Record | default._bimi.mail | v=BIMI1; l=https://www.<root_domain>/static/0/email/img/bimi-logo.svg; a=<cert_url>; |
Automatic |
| TXT Record | default._bimi.hello | v=BIMI1; l=https://www.<root_domain>/static/0/email/img/bimi-logo.svg; a=<cert_url>; |
Automatic |
BIMI defines not just a simple favicon, but rather the official logo of the brand. To fully support such an image, you would need to buy a certificate that costs from 650 to 1500 USD per year at the time of writing. There are two types: VMC (Verified Mark Certificate) for registered trademarks, and CMC (Common Mark Certificate) (cheaper one) for any logo. DigiCert and Entrust Datacard are the active BIMI-authorized certificate authorities.
If you set the BIMI record without the certificate, the image might still show up in some email clients, like the FastMail. It's just important that the used SVG file is of the SVG Tiny Portable/Secure (SVG Tiny PS). You can use https://makebimi.com/ to generate the SVG of an appropriate size and upload the logo to your website, e.g. under site_static/email/img/bimi-logo.svg.
Also, for BIMI, DMARC must use an enforcement policy (p=quarantine or p=reject) with pct=100.
Final words
So if you want to maximize your email deliverability, have two subdomains for the SaaS or web platform, and make sure to set all the guardrails as recommended:
| Record/Setting | Direction | Purpose |
|---|---|---|
| MX record | Incoming | Tells senders where to deliver mail to your domain |
| SMTP server setting (in your email client) | Outgoing | Where you connect to send mail from your account |
| SPF record | Outgoing (verification) | Tells receivers which servers are authorized to send mail claiming to be from your domain |
| DKIM record | Outgoing (verification) | Cryptographically signs outgoing mail so receivers can verify it wasn't tampered with |
| DMARC record | Both (policy) | Tells receivers what to do if SPF/DKIM checks fail |
| BIMI record | Outgoing | Lets you display your brand's logo next to your emails in supporting inboxes |
Cover picture by cottonbro studio
26 Sep 2026 5:00pm GMT
djust 1.2: More Django-Compatible, Much Faster
djust 1.2 passes 98.6% of Django's own template test suite, renders loop-heavy templates up to 45x faster than earlier releases, and adds class-level components, djust init and component-level testing.
26 Sep 2026 2:20am GMT
25 Sep 2026
Django community aggregator: Community blog posts
Issue 356: New technical governance approved for Django
News
PyCon US 2026 Recap and Recordings
All PyCon US 2026 talks are now on YouTube, along with a highlight reel and a full recap of the first year in Long Beach, which drew 1,901 attendees from 58 countries. PyCon US 2027 returns to Long Beach, May 12-18.
DSF member of the month - Ken Whitesell
Forty-five years a developer, now retired and answering forum questions so the core team doesn't have to, which he counts as a contribution in itself. He builds browser-based board game engines with Channels and HTMX, and his advice fits on a sticker: don't fight the framework.
Django Software Foundation
New Technical Governance Approved
The Steering Council and the DSF Board both approved DEP 19, which simplifies Django's technical governance and swaps narrow eligibility rules for a broad set of qualities a Steering Council member might have. Documentation updates come next.
Proposed change to DSF voting membership
Quorum is currently measured against every member on the rolls, which only gets harder to reach as membership grows. The proposal counts members who voted in the last two years, lets everyone else opt in, and takes nobody off the rolls. Comment by October 7.
Wagtail News
Experiments with MCP in Wagtail
A new experimental MCP package for Wagtail and how it came about.
Updates to Django
Today, "Updates to Django" is presented by Raffaella from Djangonaut Space! 🚀
Last week we had 20 pull requests merged into Django by 13 different contributors - including 3 first-time contributors! Congratulations to jgoneit, Philip Sørensen and ddelange for having their first commits merged into Django - welcome on board!
News in Django 6.1:
- Fixed a bug where the
fields.W225system check incorrectly warned that null has no effect on GeneratedField (#37348). - Fixed a bug where
FETCH_PEERSdid not batch instances loaded byselect_related(), causing unnecessary queries (#37344).
News in Django 6.2:
- Upstream support for PostgreSQL 15 ends in November 2027. Django 6.2 supports PostgreSQL 16 and higher.
django.db.models.GenericIPAddressFieldnow validates IPv6 input strictly when saving and querying. Invalid IPv6 addresses now raiseValidationErrorinstead of being silently accepted, and surrounding whitespace is stripped.- The undocumented
EmailValidator.validate_domain_part()method is deprecated in favor of the newvalidate_domain()method.
Thanks to all the collaborators for the good work done on the PRs merged last week. Thanks also to the volunteer reviewers who contributed: Mykhailo Havelia, Simon Charette and Mike Edmunds.
Articles
DjangoCon Chicago 2026 Highlights
Five Caktus folks pick their favourites from Chicago: the Dawn Wages and Sarah Boyce keynotes, browser features that cut JavaScript, Django 6.1's auto-prefetching for N+1 queries, and the PostgreSQL Anonymizer extension found in a hallway conversation.
Open Source Maintainership in an LLM world
When a pull request had to be carved out of granite with a chisel, the effort filtered out the frivolous ones. Frank Wiles on the slop tsunami that replaced it, and what maintainers can do: gate contributors, close the tracker on weekends, and reject clearly and kindly.
Markdown in /src
Carson Gross argues the prompts behind LLM-written code are thrown away, unlike the source a compiler keeps. His fix is a /src/md directory of human-curated Markdown, checked in beside the code and tests that get generated from it.
Building an MCP Server for Your Django App: What We Learned Doing It for Real
Lessons from shipping one, including an agent that fired a send_email tool early and mailed customers half-built data. Start read-only, add writes with dry runs and audit logs, and expect SynchronousOnlyOperation until you wrap ORM calls in sync_to_async.
Django Fellow Reports
Django Fellow Report - Natalia
Natalia reviewed 11 PRs, four of them sponsorship page improvements on djangoproject.com, and kept two security patches moving. She also reverted the system check for null on GeneratedField, dug into ORM internals to triage a related migrations ticket, and opened a forum proposal to swap Django's custom Sphinx version annotations for Sphinx's standard wording.
Django Fellow Report - Jacob
Jacob reported in from the Django on the Med sprints this week. He triaged 9 tickets (including several composite primary key bugs), reviewed 10 PRs, opened 5 tickets and 6 PRs (among them dropping GDAL 3.3/3.4 support), and weighed in on forum threads about newcomer sprints and security release notes.
Django Fellow Report - Sarah
Sarah had a busy week: she triaged six new tickets, reviewed 14 pull requests across django/django, djangoproject.com, and the DSF working groups repo, and worked through five security reports. On the authoring side, she opened a PR to document Django's new annual release cycle in the release process docs, and another to finally allow multiline template tags, a long-requested change that closes two tickets, one of them over a decade old.
Django Forum
Running Django on MariaDB or another database? Yearly survey (10 min)
The MariaDB Foundation's yearly survey is open to Postgres, SQLite, and MySQL users too this year, with some questions split by role. It is anonymous, takes under 10 minutes, closes in mid-October, and the results are published as a public report.
Django Job Board
A new Django role at The Developer Society leads the board, alongside machine learning work at Provision and a backend seat at The Cruise Brothers.
🆕 Django Developer at The Developer Society
Machine Learning Engineer (Hybrid) at Provision
Django Developer at The Cruise Brothers
Projects
7tg/django-admin-mcp
Add a mixin to your ModelAdmin classes and MCP clients get CRUD, admin actions, and relationship traversal, all within Django's existing permissions, with only Django and Pydantic as dependencies.
viewflow/seedkit
Build any Django app (a SaaS, a dashboard, or an API) from a single sentence. An agent skill that wires packages, splits dev/prod settings, and adds CI.
25 Sep 2026 3:00pm GMT