<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Basic Technology</title>
    <description>The latest articles on DEV Community by Basic Technology (@basictechnology).</description>
    <link>https://dev.to/basictechnology</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4028202%2F9a065be5-4455-4516-812b-4618fe0b7800.png</url>
      <title>DEV Community: Basic Technology</title>
      <link>https://dev.to/basictechnology</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/basictechnology"/>
    <language>en</language>
    <item>
      <title>You warmed the domain up right and reputation held. Six months on it started sliding again. It was the list.</title>
      <dc:creator>Basic Technology</dc:creator>
      <pubDate>Sat, 01 Aug 2026 01:43:43 +0000</pubDate>
      <link>https://dev.to/basictechnology/you-warmed-the-domain-up-right-and-reputation-held-six-months-on-it-started-sliding-again-it-was-hj5</link>
      <guid>https://dev.to/basictechnology/you-warmed-the-domain-up-right-and-reputation-held-six-months-on-it-started-sliding-again-it-was-hj5</guid>
      <description>&lt;p&gt;The last two posts were about dragging a self-hosted sending domain to the inbox. Auth first, then the slow warm-up. Mine got there, and it stayed there for a good while. Postmaster Tools said High, campaigns landed, I stopped thinking about it.&lt;/p&gt;

&lt;p&gt;Then months later placement started slipping on a list I had barely touched. Same auth records, same IP, same warm domain that had been fine all spring. Nothing in my setup had changed. The thing rotting underneath was the list itself, and it had been rotting quietly the whole time.&lt;/p&gt;

&lt;p&gt;It took me too long to accept this. A list is not a static asset you clean once. It decays on its own whether you send to it or not, because the people on it change jobs, abandon inboxes, and mistype their address the day they signed up. Leave it alone for six months and a chunk of it is already dead. You just can't see which chunk until it starts costing you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every bounce means the same thing
&lt;/h2&gt;

&lt;p&gt;A bounce isn't one thing. It's a category, and the categories don't all behave the same.&lt;/p&gt;

&lt;p&gt;A hard bounce is permanent. The mailbox does not exist, you get a 550 5.1.1 user unknown back, and that address should come off the list the moment it happens. Keep mailing it and you are telling Gmail you send to addresses you never verified, which is exactly what a spammer working off a scraped file does.&lt;/p&gt;

&lt;p&gt;A soft bounce is temporary. Mailbox full, a 451 because the receiving server is greylisting you, a transient DNS hiccup. Those are fine once. The mistake is treating "soft" as "ignore." If an address softs on every send for two weeks, that is not temporary at all. The mailbox is full and nobody is ever coming back to empty it. Set a threshold, something like three or four consecutive softs, and retire it like a hard bounce after that.&lt;/p&gt;

&lt;p&gt;The number the providers actually watch is the rate. Push past roughly two percent bounces on a send and you look like someone mailing dead data, and they start scoring the whole domain down for it, not just dropping the bad addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The addresses that don't bounce are the ones that hurt you
&lt;/h2&gt;

&lt;p&gt;Bounces are the easy problem because they announce themselves. The dangerous addresses are the ones that accept your mail and say nothing.&lt;/p&gt;

&lt;p&gt;Spam traps are the clearest example. There are two kinds and neither one bounces. A recycled trap is an address that belonged to a real person once, went dead, and got repurposed by the mailbox provider specifically to catch anyone still mailing stale data. A pristine trap never belonged to anyone at all, it was seeded on a web page to catch scrapers. Both accept the message, return a clean 250, and file you as a sender who does not know their own list. You will not find them by looking. That is the point of them.&lt;/p&gt;

&lt;p&gt;Then there are the merely dead. Not traps, just inboxes nobody opens anymore. They don't hurt you as loudly, but they drag down the one signal reputation is actually built on. That signal is whether people open and click. And given enough time abandoned inboxes are exactly what get recycled into traps, so today's dead weight is next year's landmine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What verification can't do
&lt;/h2&gt;

&lt;p&gt;List verification is the usual first move, and it is a good one. Mechanically it does an MX lookup on the domain, opens an SMTP conversation with the receiving server, and walks up to RCPT TO without ever sending a message. The server answers 250 if the mailbox is real or 550 if it isn't, and you drop the 550s. That genuinely clears out the obviously dead addresses before they cost you a bounce.&lt;/p&gt;

&lt;p&gt;What it cannot do is the part people assume it does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A catch-all domain accepts every address at that domain, real or not, so verification gets a 250 for total garbage and marks it valid.&lt;/li&gt;
&lt;li&gt;Traps return 250 as well, by design, so no verifier can flag them.&lt;/li&gt;
&lt;li&gt;Greylisting and rate limits hand back ambiguous answers that get scored as unknown, not clean.&lt;/li&gt;
&lt;li&gt;Probe a large list hard from a single IP and the pattern itself looks like a directory-harvest attack, and you can get that IP blocked for running the check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So verified is not the same as safe. It means you removed the addresses that admit they are dead. The toxic ones sail straight through. Treat verification as a floor to stand on and nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real fix is the same lever as the warm-up
&lt;/h2&gt;

&lt;p&gt;If reputation is built on engagement, then the thing that keeps a list healthy is the same thing that warmed the domain up in the first place. You stop mailing people who stopped caring.&lt;/p&gt;

&lt;p&gt;A sunset rule does the work. Pick a window that fits your cadence, four months of no opens or clicks is a reasonable start, and anyone past it goes into a re-engagement send. One honest "still want these" email. The ones who open come back onto the active list, and the silence gets suppressed, not deleted, just stopped. It feels wasteful the first time you cut a few thousand people who might theoretically return. They won't, and while you wait for them to they are teaching every mailbox provider that your average recipient ignores you.&lt;/p&gt;

&lt;p&gt;That is the trap in the slow version of this. A dirty list never fails on a single send. It degrades your domain reputation a little at a time, so by the time placement is visibly bad you have already spent weeks training Gmail to distrust you. Recovery is slow work after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three legs, and the third one is the one you forget
&lt;/h2&gt;

&lt;p&gt;Auth proves the mail is really from you. The warm-up earns you enough trust to land. Hygiene is what keeps that trust from leaking away while you are busy not thinking about it. Skip the third leg and the first two stop mattering, which is the whole story of the domain I thought was finished last spring.&lt;/p&gt;

&lt;p&gt;I found it the boring way, by finally exporting the list and sorting by last-open date after ignoring it for months. Roughly a third of it had not opened anything in over half a year, sitting there deliverable and useless, dragging the average down for everyone who did still read the mail. Cutting them felt like deleting money. Placement recovered within a couple of weeks. Do the boring version on a schedule and you never have to have that particular afternoon.&lt;/p&gt;

</description>
      <category>email</category>
      <category>selfhosted</category>
      <category>devops</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>You fixed SPF, DKIM and DMARC and email still lands in spam. Here's why.</title>
      <dc:creator>Basic Technology</dc:creator>
      <pubDate>Sat, 18 Jul 2026 07:04:20 +0000</pubDate>
      <link>https://dev.to/basictechnology/you-fixed-spf-dkim-and-dmarc-and-email-still-lands-in-spam-heres-why-a4f</link>
      <guid>https://dev.to/basictechnology/you-fixed-spf-dkim-and-dmarc-and-email-still-lands-in-spam-heres-why-a4f</guid>
      <description>&lt;p&gt;Last post I got the auth records green on my side-project mail. SPF, DKIM, DMARC all passing, and I figured the hard part was behind me.&lt;/p&gt;

&lt;p&gt;It wasn't. The first real campaign, about four thousand people, mostly never reached an inbox. Nothing bounced and nothing got rejected, it just got held back and quietly filtered. Every record still validated. Auth was perfect. Placement was terrible.&lt;/p&gt;

&lt;p&gt;That gap took me an embarrassingly long time to understand, so here it is up front: authentication proves the mail is really from you. It says nothing about whether anyone should want it. A domain that started sending three days ago has no track record, and to Gmail one suddenly pushing 4,000 messages looks exactly like a compromised box or a spammer who registered a throwaway that morning. So it gets treated like one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reputation is a separate thing from auth, and it starts at zero
&lt;/h2&gt;

&lt;p&gt;There are two reputations in play and neither of them cares that your DKIM signature validates.&lt;/p&gt;

&lt;p&gt;Your &lt;strong&gt;IP reputation&lt;/strong&gt; is tied to the address the mail actually leaves from. On a shared SES-style pool you inherit whatever the neighbours have been doing lately. On a dedicated IP you start from nothing.&lt;/p&gt;

&lt;p&gt;Your &lt;strong&gt;domain reputation&lt;/strong&gt; is tied to the domain in your From address, and it follows you everywhere, across every IP you ever send from. This is the one that matters long term, and on a fresh domain it is a blank page.&lt;/p&gt;

&lt;p&gt;And a blank page doesn't get read as neutral. For a brand-new domain the default lean is "suspicious until proven otherwise," because the overwhelming majority of new domains sending real volume genuinely are junk. You're guilty until you build up a history of people wanting your mail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it takes weeks, not an afternoon
&lt;/h2&gt;

&lt;p&gt;You can't speed-run this, and the mailbox providers smooth it out on purpose so you can't fake a spike. Roughly how it plays out on a fresh domain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Days 1 to 7:&lt;/strong&gt; almost no reputation data exists, so most mail defaults to spam. That feels broken the first time you watch it happen, but it's just day one with nothing on the record yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days 7 to 21:&lt;/strong&gt; if the people you reach actually open and click, the signal accumulates and placement climbs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days 21 to 45:&lt;/strong&gt; reputation stabilises, inbox placement gets predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days 45 to 60:&lt;/strong&gt; mature domain, normal volume is sustainable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing hinges on that engagement signal, which is where most people get the strategy backwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send to your most engaged people first
&lt;/h2&gt;

&lt;p&gt;Everyone's instinct is to warm up by dribbling a little out to the whole list. That's the wrong move. What you actually want in the first two weeks is the highest open rate you can manufacture, because reputation gets built almost entirely on whether people open and click.&lt;/p&gt;

&lt;p&gt;So you send to your most engaged contacts first. The people who opened something in the last week, plus your own team while you're at it. Start with a few hundred a day to that segment only, then a thousand or two, doubling every day or so for as long as engagement holds and complaints stay near zero. The moment bounces or spam reports tick up, you stop climbing and sit at that volume until it settles. There's no schedule to hit. You keep an eye on the bounce and complaint numbers and move up when they let you.&lt;/p&gt;

&lt;p&gt;Two ways to blow the whole thing up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mailing a cold or purchased list on a new domain.&lt;/strong&gt; This is the single fastest way to kill a domain that still exists. Old lists are seeded with spam traps, addresses that were real once and got repurposed by the mailbox providers specifically to catch anyone mailing dead data. An established domain can absorb a couple of those. A fresh one has no reputation to spend, so scrub the list before it ever touches a cold domain. On a fresh domain one bad send is not a bad day, it is the domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impatience.&lt;/strong&gt; Skipping the ramp because it feels slow is exactly how "Postfix took an afternoon" turns into three weeks of wondering why nobody's replying. You can stand the server up in an afternoon. Earning enough trust to actually land in the inbox is the part that takes weeks, and there is no version of it you get to rush.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the real numbers, not your gut
&lt;/h2&gt;

&lt;p&gt;Don't guess at how it's going. Gmail Postmaster Tools gives you domain reputation, IP reputation, spam rate and auth pass rate straight from Google. Microsoft SNDS covers the Outlook side, per-IP complaint rate and spam-trap hits. What I actually react to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth passing under ~95% in Postmaster means SPF or DKIM is failing intermittently. Go re-check it before anything else.&lt;/li&gt;
&lt;li&gt;Spam rate over 0.3% is a content or list problem, not a ramp problem.&lt;/li&gt;
&lt;li&gt;A volume spike you didn't schedule means something is sending on its own. Find it, then back off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is glamorous. Warmup is the least exciting part of running your own sending, mostly patience and a spreadsheet. But it is the whole difference between a domain that reliably hits the inbox two months from now and one you have to burn and start over on. I have started over. Do the boring version the first time.&lt;/p&gt;

</description>
      <category>email</category>
      <category>devops</category>
      <category>selfhosted</category>
      <category>deliverability</category>
    </item>
    <item>
      <title>Self-hosting your email? SPF, DKIM and DMARC decide your inbox rate</title>
      <dc:creator>Basic Technology</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:25:40 +0000</pubDate>
      <link>https://dev.to/basictechnology/self-hosting-your-email-spf-dkim-and-dmarc-decide-your-inbox-rate-4f2l</link>
      <guid>https://dev.to/basictechnology/self-hosting-your-email-spf-dkim-and-dmarc-decide-your-inbox-rate-4f2l</guid>
      <description>&lt;p&gt;I moved outbound email for a couple of side projects off a hosted ESP last year, mostly to stop paying per-seat for what is basically a solved problem. Postfix took an afternoon. Then I sent the first real batch and watched Gmail quietly file most of it under spam and throttle the rest to a trickle. That's when it landed that "I configured SPF" and "Gmail trusts me" are two completely different sentences.&lt;/p&gt;

&lt;p&gt;Deliverability is the part that quietly eats your week. Here's how I think about it now, plus the actual records that get most of your mail into the inbox instead of the spam folder.&lt;/p&gt;

&lt;p&gt;Two things earn you a spot in the inbox, and providers weigh them separately. One is proving the mail is genuinely from you, which is pure crypto and DNS and takes about a weekend. The other is a track record of people wanting your mail, which is behavior, and that takes months. Fail the first and you never get to play for the second. Gmail bins you before anyone reads the subject line.&lt;/p&gt;

&lt;h2&gt;
  
  
  SPF, DKIM, and DMARC each do one job
&lt;/h2&gt;

&lt;p&gt;People list these as one checklist item. They're not interchangeable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SPF&lt;/strong&gt; is a DNS record naming the IPs allowed to send for your domain. One catch trips almost everyone. It checks the &lt;em&gt;envelope&lt;/em&gt; sender, the Return-Path / MAIL FROM your servers negotiate, not the &lt;code&gt;From:&lt;/code&gt; address your recipient actually reads. So on its own it can't stop someone spoofing your domain in the visible From, and it breaks the moment your mail is forwarded and the relaying IP changes. Leave it off entirely, though, and some receivers won't bother evaluating the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com.  IN TXT  "v=spf1 mx ip4:203.0.113.10 -all"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DKIM&lt;/strong&gt; signs each message with a private key and publishes the matching public key in DNS at a selector. This is the strong one. It survives ordinary forwarding, which SPF can't, and it proves the signed headers and body weren't altered in transit. There's a caveat every mailing-list operator will remind you of. If a forwarder rewrites what it signed, like a list appending a footer or tagging &lt;code&gt;[list-name]&lt;/code&gt; onto the subject line, the signature breaks too, and that gap is exactly what ARC was built to patch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mail._domainkey.example.com.  IN TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki...QAB"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;DMARC&lt;/strong&gt; ties the two together, and its real mechanism is alignment, not a plain pass or fail. It passes if SPF or DKIM passes &lt;em&gt;and&lt;/em&gt; the passing domain lines up with the &lt;code&gt;From:&lt;/code&gt; address the recipient sees. That's why "SPF passed but DMARC failed" is a real and maddening thing: your envelope domain checked out, it just didn't match the visible From. When nothing aligns, DMARC tells the receiver what to do, and if you ask for them, it emails you reports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_dmarc.example.com.  IN TXT  "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start at &lt;code&gt;p=none&lt;/code&gt;, but include the &lt;code&gt;rua=&lt;/code&gt; address from day one. A bare &lt;code&gt;p=none&lt;/code&gt; sends you nothing, and you'll assume it's broken. Watch the aggregate reports for a week or two, then tighten to &lt;code&gt;quarantine&lt;/code&gt; and eventually &lt;code&gt;reject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The one that cost me an actual day was dumber than any of this. A single wrong character in the DKIM record when I pasted it into DNS. It looked configured. The signature just silently failed to verify, so I'd quietly lost my strongest trust signal and didn't notice until deliverability slipped days later. Now I always confirm the published key matches the private one before I trust it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short TXT mail._domainkey.example.com
opendkim-testkey &lt;span class="nt"&gt;-d&lt;/span&gt; example.com &lt;span class="nt"&gt;-s&lt;/span&gt; mail &lt;span class="nt"&gt;-vvv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;opendkim-testkey&lt;/code&gt; is the one that actually catches the mismatch. &lt;code&gt;dig&lt;/code&gt; only shows you what's published, which looks fine even when it's subtly wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reputation starts cold, so warm the domain and the IP
&lt;/h2&gt;

&lt;p&gt;Even with perfect auth, a brand-new IP has no reputation. And it isn't only the IP: Gmail and Outlook lean heavily on &lt;em&gt;domain&lt;/em&gt; reputation now, and the domain is the part that follows you when you change IPs. So warm both.&lt;/p&gt;

&lt;p&gt;Blast your whole list on day one and you'll get throttled or binned. When I brought that fresh IP up, day one was maybe 50 messages, to my most engaged contacts only. As long as bounces and complaints stayed flat I roughly doubled the next day: 50, then a bit over 100, then a few hundred, backing off the instant either number twitched. A clean few thousand a day took about two weeks. For real volume, tens of thousands, plan on a month or more. The gate is a clean previous step, not the calendar.&lt;/p&gt;

&lt;p&gt;And never send to a bought or scraped list. Spam traps don't care how good your auth is, and that's the fastest way to tank a domain you can't easily recover.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unglamorous parts beat good copy
&lt;/h2&gt;

&lt;p&gt;Own your Return-Path and actually process bounces, so you stop hammering dead addresses within a send or two instead of a month later. Feedback loops are worth the afternoon they take to set up (Microsoft's JMRP, Yahoo's CFL, and the like), so you hear about spam complaints directly instead of inferring them from a sagging open rate. And keep the list clean. Hard bounces get dropped on the spot, and anyone who hasn't opened in six months gets pruned.&lt;/p&gt;

&lt;p&gt;Content and subject lines matter, but they sit way down this list. Every "why is my mail in spam" fire I've actually chased came back to auth or a dirty list. Almost never the copy.&lt;/p&gt;




&lt;p&gt;This post is adapted from a longer write-up I keep on my own KB, &lt;a href="https://acellesend.com/kb/articles/how-to-set-up-spf-dkim-and-dmarc-records" rel="noopener noreferrer"&gt;the full record-by-record version&lt;/a&gt;, if you want every DNS line and gotcha spelled out. Fwiw, I build AcelleMail, a self-hosted email marketing tool (commercial, not open source, before anyone asks), so I stare at this more than is healthy. None of it is product-specific, though. Postfix, Sendy, or something you rolled yourself: same three records. Get those right first.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>email</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
