Preface
With some free time on my hands, I felt that a home data center should at least have a mailbox with its own domain name suffix, so I prepared to build a mail server myself. Regarding the choice of software, I investigated a lot. First, I looked into EwoMail, but the official recommendation was CentOS 7/8, so I installed CentOS 8. Then I found out that after its life cycle ended on 2021-12-31, the Linux community no longer maintained this version, so the yum command could not be used. Even after changing the source, a bunch of errors were reported when using the installation script, which made me angry, so I switched directly.
I also looked into some other open-source mail server software, such as Postal, mailcow, etc. They were either too single-featured (only outbound, no mailbox management, etc.) or too feature-rich and consumed too many resources. Refining my requirements: support webmail, support sending and receiving mail, support Docker deployment, and not consume too many resources... After searching around, I found that poste can meet all my requirements.
Introduction to poste
poste is an open-source mail service software that can easily build: SMTP + IMAP + POP3 + Anti-spam + Anti-virus + Web Administration + Webmail, supporting the following features:
- Native implementation of SPF, DKIM, DMARC, SRS, with a simple wizard
- Anti-virus engine (ClamAV) for detecting trojans, viruses, and malware
- Built-in spam filter (RSPAMD)
- Webmail client over HTTPS (Roundcube)
- Email redirection, auto-responders, and other filtering via Sieve scripts (managed by email owners, every action can be scripted)
- Quotas for limiting mailbox space or number of emails
- Web administration with different privileges for system administrators, domain administrators, and email owners.
- Built-in autodiscover for Microsoft products, Thunderbird
- Diagnostics to help set up domains and mail servers correctly
- By default, all passwords are stored as salted SHA512 hashes (5000 rounds). Attackers will find it very difficult to crack your password.
It looks very powerful, and crucially, it natively supports Docker deployment. My home mail server will rely on it.
Network environment requirements
First, let's confirm the network environment requirements when using poste.io to build a mail server. Let's look at the official explanation from poste:

These ports will be mapped using the -p parameter when creating the Docker container, but which of these ports are actually required?
Let's first look at the check results of the poste mail server I built. The red boxes indicate abnormal check results:

Analysis of the causes of abnormalities:
LE (Let’s Encrypt)
This is probably because my home broadband does not have port 443, so it cannot be used. This has little impact, it just means I cannot rely on Let's Encrypt to automatically apply for and renew certificates. Each account on Tencent Cloud has a quota of 20 free one-year certificates, so I'll just leverage that.
outbound port 25
The outbound port 25 shows an issue, but actually it is fine. It mainly tests the connectivity to Gmail's port 25, so it is normal to have an issue here.
inbound 80,443,4190
Ports 80 and 443 are definitely not available on home broadband, so it's normal to have issues. They can be changed to other ports when setting up Docker. Port 4190 is optional, and its abnormality has no impact, so I'll just ignore it.
The above results show that my home broadband can send and receive mail normally without ports 80, 443, and 4190, so these ports are not required. The remaining ports 110, 143, 587, 993, and 995 are actually used by mail clients to connect to the mail server using various protocols (POP3, IMAP) and their corresponding TLS versions. Even if some are missing, it only means that mail clients cannot directly connect to the mail server in the corresponding way, but at least webmail can be used, which means they are not strictly required either.
There is still port 25 left. Is this one required? For building a mail server, port 25 is indeed required. To be specific, SMTP port 25 is divided into two directions: ”inbound” and ”outbound” (as shown in the test items in the figure above). For sending mail, outbound is required; for receiving mail, inbound is required.
This needs to be explained starting from the SMTP workflow.
Let's first look at the entire process of an email sent by the sender via the SMTP protocol reaching the receiver:

To simplify it a bit more:

As can be seen from the figure above, the first step from the mail client to the sender mail server does not necessarily require port 25; other ports can also be used (if port 25 is used, this is the inbound direction for the sender mail server). The second step actually only specifies that the receiver mail server must use port 25 to receive mail (the destination port for the sender mail server to send requests to the receiver mail server is 25, which is the outbound direction for the sender mail server), while there is no requirement for the source port of the request sent by the sender mail server.
In a nutshell: SMTP port 25 outbound means the mail server uses the SMTP protocol to access other people's port 25; SMTP port 25 inbound means others use the SMTP protocol to access the mail server's port 25.
For the specific process of the sender mail server sending mail to the receiver mail server, you can refer to the figure below. This is the packet capture process when I sent an email to a 139 mailbox using my own mail server. You can see that my mail server (sender mail server) first established a TCP 3-way handshake with port 25 of the 139 mail server (receiver mail server), and then the 139 mail server actively initiated an SMTP request to my mail server with port 25 as the source port:

Generally speaking, cloud server providers will block port 25 of cloud hosts. This blocking of port 25 actually refers to outbound blocking, which is to prevent you from setting up a mail server at will and starting to send spam. After all, because the SMTP protocol itself appeared relatively early, it lacks a verification mechanism for the sender. The lack of an authentication mechanism makes the SMTP protocol permissive, which naturally includes spam.
In fact, even if the cloud provider blocks port 25 (outbound), it only prevents your mail server from directly sending mail using the SMTP protocol. Since SMTP port 25 inbound is not blocked, receiving mail sent by other mail servers using SMTP is still perfectly fine.
If you want to apply to the cloud provider to unblock port 25, first, the cloud host you purchased needs to be a relatively expensive annual prepaid cloud host type. Second, applying for unblocking also depends on luck to see if it can pass the review. Because the Tencent Cloud host I bought is the cheapest lightweight server, I am not even qualified to apply for unblocking, so in the end, I could only use home broadband.
Configuring domain name resolution records
To set up a mail server, you must first configure the relevant DNS records with your domain registrar. Taking the domain example.com as an example, the DNS records that need to be added to the example.com domain at the domain registrar are shown in the figure below:

The host record ”@” corresponding to the MX and TXT types above indicates that the email suffix is example.com. If the email suffix is another subdomain, such as “@mail.example.com", then the "@" here should be changed to "mail.example.com”. The purpose of the three CNAMEs is to provide server addresses for mail clients to access via different protocols, making them easier to remember. In fact, they are not mandatory; using mail.example.com directly works just the same.
The first three records must be configured: the A record and the MX record combined allow your mailbox to receive emails normally. The A record and the TXT record (this TXT should originally be an SPF record, but since some domain registrars do not support SPF records, TXT is used instead with the same content) combined allow the emails sent from your mailbox to pass the basic SPF security check of the recipient's mail server.
As long as outbound traffic on SMTP port 25 is available, your mailbox can normally send emails to the mail servers of various email providers. However, whether they can ultimately enter the recipient's inbox or even the spam folder is another matter, which we will discuss separately in the next section, ”Email Scoring”.
Additionally, because my home broadband's egress address is a dynamic public IPv4, I need to use a tool to synchronize the egress public address to Tencent Cloud's DNSPod domain management in real-time. I accomplished this using the dynamic domain tool built into the iKuai router. This is very important because it involves SPF records, which will be explained in detail in the next section.
Mail score
In the previous section, we mentioned that as long as outbound traffic on SMTP port 25 is available, emails sent from your mailbox to other email addresses can reach the destination mail servers (thanks to the inclusiveness of the SMTP protocol). However, because of this inclusiveness, spam can also get through. Therefore, each email service provider has its own scoring system to identify whether an email is spam. Although the scoring systems vary, some of the most basic judgment steps are consistent:
rDNS
Reverse DNS (rDNS) resolves an IP address back into a domain name. This is mainly used by the receiving server to perform a reverse lookup on the source IP address of the received email (called reverse because forward DNS resolves a domain to an IP) and then compare it with the sender's email domain. There are generally two possible outcomes:
Found but inconsistent, indicating a spoof.
Not found, indicating that no PTR record is configured on the DNS of the sender's domain. Although they might not necessarily be malicious, they are certainly not legitimate. In this case, different providers handle it differently; some may reject or discard it directly, while others may just deduct points from the reputation score.
Since I am on home broadband, I cannot set up a PTR record, so I fall into the second category. Based on my test results, Gmail and QQ Mail cannot receive them at all (they don't even qualify to enter the spam folder), but 163 Mail and 139 Mail work fine.
SPF record
Sender Policy Framework (SPF) aims to prevent senders from forging sender addresses at will. The implementation principle is very simple: the recipient's mail server queries the IP address corresponding to the sender's email suffix and then compares it with the sender's IP address of the received email to see if they match. This requires setting up an SPF record or a TXT record in the DNS zone corresponding to the sender's suffix domain (see the previous section). There are various syntaxes for SPF record values to specify different IPs; you can search for the detailed syntax yourself.
Note that there may be some differences in the syntax symbols of different domain registrars. For example, regarding the ”mx all” in the previous section, which means rejecting all other IP addresses except the IP resolved by the MX record, this ”all” is the way it is written on Tencent Cloud's DNSPod. If it is Alibaba Cloud Mail, it is ”-all”. Therefore, when adding SPF records, please check the relevant instructions of your domain registrar.
DKIM
DomainKeys Identified Mail (DKIM) is used to add a sender's digital signature to the email header of the email content. After receiving the email, the recipient needs to verify the digital signature in the email header. If the verification is successful, the email was sent by the sender themselves; otherwise, it is forged.
The basic working principle of DKIM is also based on traditional key authentication. It generates two sets of keys: a public key and a private key. The public key is stored in the DNS, while the private key is stored on the sending mail server. The private key is automatically generated, attached to the email header, and sent to the sender's server. The public key is placed on the DNS server for automatic retrieval. The receiving server will receive the private key attached to the email header and retrieve the public key itself from the DNS, then compare them to check if the sender's domain is legitimate. If it is not, the email is determined to be spam.
Poste's DKIM needs to be generated in the web console (which we will discuss later) and then added as a TXT record (which is actually the public key mentioned above).
DMARC
DMARC (Domain-based Message Authentication, Reporting and Conformance standard) is a solution based on SPF and DKIM. It is essentially a set of agreements: the email sender publicly indicates the sending servers they use through their DNS (via SPF) and digitally signs the sent emails using a private key (via DKIM); the email recipient checks whether the received email comes from an authorized mail server of the sender (by querying the SPF record corresponding to the email domain) and whether the digital signature is authentic (based on the public key provided by the DKIM record), determines how to handle emails that fail the checks (reject them or send them to the spam folder), and decides whether to send a notification email to the sender's mailbox.
How to configure
Putting the above 4 points into practice actually corresponds to 4 records in the sending email's DNS: a PTR record for (rDNS), an SPF record (or TXT record) for SPF, a TXT record for DKIM, and a TXT record for DMARC.
Some VPS providers can directly provide PTR records, but it is difficult in China. For instance, Tencent Cloud has requirements on the type of cloud host for PTR records, and 5 PTR records cost 1,500 RMB per year.
I have already explained how to add SPF records in the previous section. You can directly add them as an SPF record type, or you can add them as a TXT record; the content is exactly the same.
DKIM is added as a TXT record, and we will discuss the specific content to be added later.
DMARC is added as a TXT record. Taking Tencent Cloud DNSPod as an example:
记录类型:TXT
主机记录:_dmarc
记录值:v=DMARC1; p=none; rua=mailto:[email protected]; ruf=mailto:[email protected]
Parameter explanation:
v=DMARC1
The version number of DMARC
p=none
Used to inform the recipient of what action to take when an email is detected to have a forged sender. There are 3 options: p=none; the recipient takes no action; p=quarantine; the recipient marks the email as spam; p=reject; the recipient rejects the email.
rua=mailto:[email protected]
Optional, used to tell the recipient which email address should be used to notify the sender with aggregate reports. This is like a “suggested retail price”—it is hard to say whether the recipient will accept the suggestion.
ruf=mailto:[email protected]
Optional, used to tell the recipient which email address should be used to notify the sender if an email fails SPF or DKIM checks. Similar to rua, this is also a “suggestion”.
In fact, besides the aspects mentioned above, the sender's IP address itself is also extremely important: whether it is blacklisted, whether it is a static IP, etc., are all important criteria for the recipient's scoring.
You can use some online tool websites to check whether an IP address or domain name is blacklisted, as follows:
https://mxtoolbox.com/blacklists.aspx
Installing poste via Docker
The Docker command for Poste is as follows:
docker run --name mailserver -d --restart=always --network=public-net \
--hostname "mail.example.com" \
-p 25:25 \
-p 110:110 \
-p 143:143 \
-p 465:465 \
-p 587:587 \
-p 993:993 \
-p 995:995 \
-p 4190:4190 \
-p 8080:80 \
-p 8443:443 \
-e "TZ=Asia/Shanghai" \
-e "DISABLE_CLAMAV=TRUE" \
-e "DISABLE_RSPAMD=FALSE" \
-e "DISABLE_ROUNDCUBE=FALSE" \
-e "HTTPS=OFF" \
-v /docker/poste.io/data:/data \
-v /etc/localtime:/etc/localtime:ro \
-t analogic/poste.io
--hostname "mail.example.com" \

-p 25:25 \
-p 110:110 \
-p 143:143 \“mail.example.com”-p 465:465 \
-p 587:587 \“
-p 993:993 \
-p 995:995 \“
-p 4190:4190 \
-p 8080:80 \“
-p 8443:443 \
-e “TZ=Asia/Shanghai” \”
-e "DISABLE_CLAMAV=TRUE" \-e "DISABLE_RSPAMD=FALSE" \This maps
-v /docker/poste.io/data:/data
`--network=public-net` This parameter must be specified for both the guacd container and the next-terminal container, otherwise the connection between next-terminal and guacd can only rely on `--link`/docker/poste.io/datadirectory to be mounted to the /data directory of the container
-v /etc/localtime:/etc/localtime:ro
Synchronize host clock
-t analogic/poste.io
Specify the image name, this is mainly to distinguish between the free version and the pro version
After docker runs successfully, you can directly usehttp://宿主机IP:8080/admin/ to access the poste console. Since I am using it with a reverse proxy, I used the-e "HTTPS=OFF"parameter, so it can be accessed directly via http.
Configuring poste
Initialization
Usehttp://宿主机IP:8080/admin/登录设备:

For the first login, you need to configure the public domain name of the mail server (specified by the A record of the domain name),which is mail.example.com in this example, and specify the administrator email address and password. After setting, click Submit to enter.
Creating DKIM
Under Virtual domains, select example.com on the right:

Click "create a new key" in the red box in the middle right:

The following is the generated DKIM:

In the figure above, the part in the red box
s20231026459._domainkeyis the host record of the TXT record corresponding to DKIM, and the part in the red boxk=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+dbUYLiyTlvAhyUINYw7FdHFNO8DSCMkQHbOAwQe0kWyGyDaXRRjp5LYPYawHIg+JyX+drnGepkg2w2rsN9UgNxkgKTnEmWLPGDdAwf2phjIKUT4Xw8y1TzL2nGaQK80lQWr1fNMxR8urcmiUZBCbQdPGRlqAVX1moymHd66Mk3MssvMW2WV9EjMJ5dqTplbP2NABuA+ygTtoP7zt1zo6QLTUvjsoD2hg26xtQy4DcXEtVzdlCjW22GUOwip7FyqiIgKfY2EGEzlsl7J5V+nisQzYqS6m7UUFSxqHr0EwKB8xMTvKCFofrwuWogTqsp9Gim01HDqLuTfLsHrynOhwIDAQABis the record value of the TXT record.
Setting up TLS certificates
If you want to use an email client to send and receive emails and use encrypted connection methods such as SMTPs, IMAPs, and POP3s, you need to set up the TLS certificate for the mailbox:

poste supports two methods: Let's Encrypt automatic certificate application and manual certificate upload, as shown in the red box on the right:
The Let's Encrypt method is relatively simple, as follows:

After configuration, click "save changes" below to automatically apply for a certificate. However, this method probably requires port 443. My home broadband doesn't have it, so the application failed. Therefore, I used a free certificate applied from Tencent Cloud and uploaded it manually:

The first line is the private key file of the mail.example.com domain certificate, and for the second and third lines, just select the public key certificate file of the domain name, and finally click Save changes below to save.
Note: After saving, no success message will appear; it will flash and then return to its original state:

This is normal. You can see that the certificate is already in the ssl folder of the mapped directory:

At this point, poste can already send and receive emails normally. However, while receiving emails is fine, whether sending emails can reach other people's inboxes depends on the configuration of the email scoring section mentioned earlier. You can refer to the scoring website below for the email scoring status:
Email scoring tool:https://www.mail-tester.com/
This website allows testing an email address 3 times a day. For example, my email test results:

You can see the deduction items. Orange and red should indicate the severity of the problem. For example, I lost 3.1 points on the second item. Click to open it to see the details:

You can see that the biggest deduction items are PYZOR_CHECK and RDNS. RDNS is my fatal weakness, there is nothing I can do about it. This single item alone gets the emails sent from my mailbox rejected by major email service providers. So what is PYZOR_CHECK? Looking at the explanation, it turns out that it requires the content of the sent email to be realistic. I just sent the word "test", so I guess it was looked down upon.
This test score is just for reference. For example, my score was as high as 8.9 in the last test:

But it's useless, what can't be received still can't be received.
Finally, to log in to webmail, usehttp://宿主机IP:8080/webmail/to log in to the mailbox:

Configure public network access
If you want to publish to the public network, you need to choose the most suitable publishing method based on your actual environment and the reverse proxy you use. You can refer to several of my previous articles:
1、Docker Series: Building Your Own Reverse Proxy Based on NPM Using Docker
2、Linux Panel Series: Configuring Reverse Proxy and Publishing Using Non-443 Ports
3、Home Data Center Series: Getting Cloudflare for Free via Domestic ICP-Filed Cloud Hosts to Achieve Fast Access to Domestic Sites from Abroad
4、Home Data Center Series: Getting Cloudflare for Free via Home Broadband Without Public IP to Achieve Fast Website Building (Universal)
Among them, the 1st and 2nd methods are suitable for environments with a public IP but without a legal 443 port (home broadband, non-filed cloud hosts), where non-standard ports need to be added after the URL (if using Cloudflare to build a website, no port needs to be added, but you need to customize the origin server port, which you can refer to:Home Data Center Series: Solving the Problem of Having a Public IP but No Legal 80 or 443 Ports for Website Building via Cloudflare's Origin Rules). The 3rd method is suitable for cloud hosts with ICP filing, and the 4th method is suitable for all environments (including environments without a public IP), which is also my recommended method (regardless of whether your environment has a public IP, because this method does not require running HTTPS traffic directly on the public network).
P.S.: Using the replacement function of nginx reverse proxy can hide the "pro" word on the web page, making it look a bit cleaner, but it's also not very useful...
Afterword
Finally finished writing. Writing this article was so tiring, I had to look up a lot of information, and the energy spent was equivalent to writing many ordinary articles. I'll see if there is anything else to add later. Using a home broadband mailbox to receive emails to show off is still fine, but as for sending, it depends on luck (mailboxes of regular major email providers are probably hopeless). Currently, the 163 mail and 139 mail I tested can receive emails, but Gmail and QQ mail cannot. If you really want to set up a proper mail server, you'd better use a foreign VPS that supports rDNS.
P.S.: If you only need to receive emails, you can use Cloudflare's Email Routing feature. In just a few simple steps, you can achieve the same email receiving function as a self-built mail server. For specific configuration steps, please refer to the article:Home Data Center Series: Using Cloudflare to Create an Email Alias with Your Own Domain Suffix 。