Running my own mail server
Most people rent email from Google or Microsoft. This lab receives, filters and stores its own. It is not a demo mailbox either: the lab's backup summaries, its alerts and its job-search mail all arrive this way every day. Mail is widely considered the hardest common service to self-host, which is exactly why it belongs on a portfolio site. This page shows the path a message takes in each direction, and the three DNS records that stop everyone else's mail server from treating self-hosted mail as spam.
Why self-host mail at all
Honestly, because it is hard. Running a web server is a solved afternoon. Running mail means being accountable to every other mail operator in the world, most of whom start from the position that a small unknown server is probably a spammer.
Nothing about receiving mail is difficult. The difficulty is being believed when you send. A message from a small self-hosted domain has to arrive with proof of who sent it and proof that it was not altered on the way, or it lands in a spam folder and you never find out. That proof is the DNS half of this page, and it is most of the work.
The path a message takes
Two paths, and they are not mirror images of each other.
Inbound is the straightforward direction. A message arrives from the internet at the edge, goes through a content filter that scores it and strips anything dangerous, then to delivery, which decides which mailbox it belongs in and writes it to the store. That whole path is self-hosted.
Outbound is where the honesty comes in. A mail client submits the message to the lab's submission service, authenticating first, and it then goes out through a sending relay that signs it on the way. The relay is there for a specific and unglamorous reason: home internet addresses are widely distrusted by receiving mail servers, no matter how correct your configuration is. Sending through a relay with an established reputation is what gets mail delivered rather than silently binned. The DNS records in the next sections are exactly the machine-readable statement of that arrangement, which is why they look the way they do.
The pieces
The mail server itself is docker-mailserver, a maintained bundle that packages Postfix, Dovecot and Amavis together as one unit, sized to 4 GB of memory and 2 vCPUs in this lab. Running it as one maintained bundle rather than three separately patched services is what keeps the parts that have to work together pinned to versions known to work together.
Postfix moves mail. It is the part that talks to other mail servers, accepts messages for the domain, and hands them onward. If a message is going somewhere, Postfix is moving it.
Dovecot stores mail and hands it to you. It owns the mailboxes on disk and serves them to mail clients, so it is the part your phone actually talks to.
Amavis sits between the two and inspects. It runs messages through virus and spam scanning, scores them, and passes the verdict along so delivery can act on it.
One identity everywhere
Mailboxes do not have their own passwords. Authentication goes against Active Directory over LDAPS, the encrypted form of the directory protocol, using a service account scoped only to directory lookups. Active Directory will not hand back a password hash for a lookup like that, so the check works by attempting to bind as the user with the password they typed rather than by comparing hashes, and the bind either succeeds or it does not. A mailbox uses the same account as everything else in the lab this way, and there is no second list of users to keep in step.
Webmail is Roundcube, running behind a dedicated proxy with its own database, and it sits behind the same single sign-on as the rest of the lab's web applications: Cloudflare Access, with a policy backed by Entra ID. Reaching mail in a browser uses the same login and the same multi-factor prompt as any other internal service. The certificate the directory presents for that encrypted connection is issued by the lab's own AD CS root, the same authority the secrets cluster uses.
SPF, who is allowed to send
SPF is a public list of who is permitted to send mail claiming to be from your domain. A receiving server looks up that list and checks whether the message actually came from one of them.
The published record, read from DNS on 29 July 2026:
v=spf1 include:spf.brevo.com mx ~all
Token by token. v=spf1 says which version of the rules to apply.
include:spf.brevo.com means "also accept anything the sending relay is allowed to
send from", which is what authorises the outbound path above without having to list addresses
that the relay may change. mx means "the servers that receive mail for this domain
may also send for it", which covers mail sent directly by the lab.
~all is the interesting one and it is the part people get wrong. It means anything
not covered above is a soft fail: treat it as suspicious, but do not reject it outright. The
stricter option is -all, a hard fail. Soft fail is the honest setting while you are
still confirming you have found every legitimate sending path, because the failure mode of being
too strict too early is that your own mail disappears and you do not get told. Tightening it is a
deliberate later step, taken once the reports below show nothing legitimate is failing.
DKIM, proving it was not altered
SPF says a server was allowed to send. It says nothing about whether the message arrived in one piece. DKIM covers that: the sending side signs the message with a private key, publishes the matching public key in DNS, and the receiver verifies the signature. If anything altered the signed parts in transit, verification fails.
The public key is published under a label called a selector, which is just a name that lets a domain publish several keys at once and rotate them without an outage. Two selectors are published for this domain, and rather than holding the key text directly they point at the sending relay's own published keys:
brevo1._domainkey -> b1.parsec-lab-com.dkim.brevo.com
brevo2._domainkey -> b2.parsec-lab-com.dkim.brevo.com
That pointer arrangement is deliberate and it is the normal pattern when a relay signs on your behalf. The relay holds the private key and rotates it on its own schedule; because the domain publishes a pointer rather than a copy, the rotation happens without anyone editing this domain's DNS. Two selectors rather than one is what keeps a rotation invisible from the receiver's side: the old key stays resolvable while the new one takes over.
DMARC, tying the two together
SPF and DKIM each answer a narrow question and neither says what a receiver should do when the answer is no. DMARC is the policy that ties them together. It says: check that the domain the reader sees in the From line is the same domain that SPF or DKIM actually verified, and here is what to do if not.
The published policy, read from DNS on 29 July 2026, with the reporting address shown by role rather than in full:
v=DMARC1; p=none; rua=<reporting mailbox at this domain>; fo=1
p=none is the policy, and it means: do not reject anything, but send me reports.
That is the monitoring stage rather than the enforcing stage, and choosing it on purpose is the
point. Turning on rejection before you know every path your mail takes is how organisations
silently destroy their own invoices and password resets. The sequence is to publish
p=none, collect reports until the picture is boring, then move to quarantine and
eventually reject.
rua is where receiving providers send aggregate reports: daily summaries from the
likes of Google and Microsoft saying how much mail they saw claiming to be from this domain and
whether it passed. fo=1 asks for a report whenever any check fails, rather than only
when everything fails, which is the setting that makes the reports useful while tuning.
Reading those reports properly, and the decision about when to tighten the policy, is genuinely the next piece of work here rather than something already finished.
Getting mail to a phone
Mail clients get new messages one of two ways. They can ask repeatedly, which is simple and means new mail waits until the next poll. Or they can hold a connection open and be told immediately when something arrives, which is what makes mail feel instant on a phone.
The second way is better and it is fragile in a specific way that took a while to pin down. It depends on a connection that stays open and mostly silent for long stretches, and anything in the middle that quietly closes idle connections breaks it. The symptom is not an error. It is mail that arrives promptly some of the time and with a long delay the rest of the time, which reads like a slow server rather than a dropped connection.
The fix is to keep the idle connection alive deliberately rather than assume the path will leave it alone, and the lesson generalises past mail: any long-lived connection through something you do not control needs to prove it is still there, or it will be closed underneath you and nothing will report it.