Setting Up Postal as an SMTP Server
Set up a secure and efficient SMTP server with Postal, from installation to advanced configuration.
Over the past few weeks, I’ve been exploring the idea of running my own mail server. While I had a basic understanding of how email servers work, I’d never actually set one up from scratch.
To keep things simple (and sane), I decided to focus on just one piece of the puzzle: sending email. No inboxes, no IMAP or POP – just a streamlined, send-only SMTP server.
This minimalist approach has a lot of advantages. By isolating the outbound mail component, I can reuse it across multiple projects – whether it’s bulk newsletters, transactional emails from WordPress sites, or as an external relay for my personal addresses – without dragging along a full mail stack every time.
In this guide, I’ll walk you through setting up Postal, a powerful open-source SMTP server. I’ll cover installation, configuration, and best practices for deliverability.
Get updates from my mail server journey – tips, lessons, and discoveries along the way.
Why Postal?
If you know me, you know that from my collection of server security guides, I’ve always tried to limit the use of third-party security software and prefer to build my own solutions.
I’ve always leaned towards the old school way of doing things, avoiding extra software and instead using the available server packages to suit my needs.
So why didn’t I just go with Postfix for this SMTP setup? I seriously considered it – but Postal offered too many advantages to ignore.
Unlike Postfix, Postal comes with a suite of modern features out of the box. And since this is a fairly complex project, I didn’t want to spend hours wiring together manual configs for every little piece.
Postal simplifies a lot of that – and has an active community (GitHub Discussions) where you can get support if you hit a wall.
Postal makes it easy to create multiple IP pools, so you can send emails from different IP addresses instead of being stuck with just the server’s main IP.
This is huge for:
- Separating email traffic across clients or apps.
- Managing deliverability and sender reputation.
- Assigning clean IPs to new users without affecting your existing ones.
If you're running multiple projects or managing email for clients, this kind of flexibility is a must-have.
Throughout this guide, you’ll discover more powerful features, such as its clean and intuitive web interface, its built-in integration with SpamAssassin to scan outgoing emails for spam, and even the ability to receive emails through routes (yep, we’ll cover that too).
Author's Note
Now that you know why I chose Postal as my SMTP server, I want to highlight a few key points that can make or break your setup – especially when it comes to deliverability.
A clean, trusted IP is crucial – not just for your main server, but for every IP you use in a pool. A poor reputation can lead to your emails being flagged as spam or blocked entirely.
That’s why choosing the right server provider matters. In my experience, Hetzner has the strictest email-sending policies.
Most providers block port 25 by default to combat spam, and while many will unblock it upon request, Hetzner is by far the most stringent in this regard. Their strict policies ensure that their IPs remain clean and trusted, making them an excellent choice for an SMTP server.
But even with clean IPs, don’t expect inbox success on day one. If you're using a brand-new IP, mail servers may treat it as suspicious. You’ll need to warm up the IP – sending gradually and building trust over time.
Hetzner, again, is great here. They offer Floating IPs – extra public IP addresses you can attach to your VPS in addition to the main IP.
What makes Floating IPs useful:
- They’re detached from the server itself, so if you delete or replace the server, you can reassign the same IPs to a new one.
- This helps you preserve your sending reputation and avoid restarting from scratch.
- Perfect for IP pools and advanced sending setups.
If you're using a provider other than Hetzner, be sure to check if they offer something similar.
IP Pools
If you're interested in using IP pools with Postal, I've written a separate guide that walks through how to set them up and use them effectively.
But don’t worry about that just yet – I recommend completing this setup guide first, then coming back to the IP pools once everything is up and running.
Server Preparation
Before preparing our server to run Postal, we need to deploy it first.
Choose Ubuntu 24.04 LTS as the server image – it’s stable, well-supported, and perfect for this setup.
If you're using Hetzner, you’ll notice they offer two types of servers:
- Shared CPU (resources are shared with other customers)
- Dedicated CPU (resources are reserved for you)
I highly recommend opting for the servers with dedicated CPUs, as an SMTP server is mission-critical and should never face resource limitations.
It’s also essential to enable IPv6 on your server (many modern mail servers require it) and monitor server resources to avoid bottlenecks in production.
Initial Setup
After your server is deployed, SSH into it and begin by running the following commands to perform a full update:
apt update
apt dist-upgrade
root
for the first time, remember to change your root password to something stronger afterward.Next, make sure to set the correct timezone for your server. This isn’t just for convenience – it’s important for Postal’s internal logging and email timestamps.
For example, to set the timezone to Berlin, you can run:
timedatectl set-timezone Europe/Berlin
With the timezone in place, the next step is to configure your server’s hostname.
A typical hostname consists of two parts: the server name and the domain name. Since we’re building an SMTP server, a clear and descriptive hostname helps other mail servers recognize your server’s role.
A recommended format is:
hostnamectl set-hostname mailout.example.com
Using mailout
as the server name is a good practice, as it clearly indicates to other mail servers that this server is handling SMTP functions.
For better security:
- Create a new user with sudo privileges
- Disable the root user
- Set up SSH keys for the new user
- Disable password authentication
Install Dependencies and Docker
Now that your server is updated and configured, let’s install the essential tools Postal relies on.
Start by installing the required packages:
sudo apt install git curl jq
This ensures that git
is available on your server, which we’ll use to clone the Postal installation helper repository:
sudo git clone https://github.com/postalserver/install /opt/postal/install
Then, create a symbolic link so you can run the postal
command from anywhere on the server:
sudo ln -s /opt/postal/install/bin/postal /usr/bin/postal
postal
command needs to be run with sudo
privileges or as the root user.Use a Proper Docker Setup (Not Ubuntu’s Built-in Version)
Postal runs entirely inside containers, so installing Docker Engine and the Docker Compose plugin is a major prerequisite.
But, the Docker packages included in Ubuntu’s default repositories are often outdated and may cause issues.
To avoid problems, follow my Docker installation tutorial, where I walk through installing the latest version of Docker and Docker Compose the right way – fully compatible with Postal.
Set Up MariaDB
Postal requires a database engine to store all emails and other essential configuration data, and it will automatically provision a database for each mail server you create.
We’ll run MariaDB in a container, which is the easiest option:
sudo docker run -d \
--name postal-mariadb \
-p 127.0.0.1:3306:3306 \
--restart always \
-e MARIADB_DATABASE=postal \
-e MARIADB_ROOT_PASSWORD=postal \
mariadb
This will start a MariaDB instance, listening on port 3306
. Make sure to change the root password to a strong one and store it securely, as you’ll need it later during the Postal configuration.
You can verify that MariaDB is running with:
sudo docker ps
You should see the postal-mariadb
container listed with its status as Up
.
Final Prep Steps
Once everything is in place, it's a good idea to sudo reboot
your server to make sure all changes are fully applied.
If you're using Hetzner, visit the Primary IPs tab and enable protection for your assigned IPv4 and IPv6 addresses.
Generating Configuration Files
Before starting the installation, Postal provides a tool that automatically generates the initial configuration files required for setup.
Run the following command, replacing mailout.example.com
with the actual hostname you set earlier – the one you plan to use to access the Postal web interface:
sudo postal bootstrap mailout.example.com
The command will generate three important files inside the /opt/postal/config/
directory:
postal.yml
: The main configuration file for Postal.signing.key
: A private key used by Postal to sign various internal components.Caddyfile
: The configuration file for the Caddy web server, which Postal uses to serve its web interface.
The only file that matters to us at this point is the postal.yml
file, which we’ll use to tweak and customize Postal’s configuration.
Once the files are generated, open the postal.yml
file and update the passwords for both the main_db
and message_db
sections.
Use the password you specified earlier when creating the MariaDB instance with Docker:
main_db:
host: 127.0.0.1
username: root
password: here_comes_the_password
database: postal
message_db:
host: 127.0.0.1
username: root
password: here_comes_the_password
prefix: postal
Save and close the file – that’s all we need to do for now.
/opt/postal/config
as /config
, any file paths referenced inside postal.yml
should start with /config
instead of /opt/postal/config
.DNS Setup
Correct DNS configuration is critical. If your DNS isn’t properly set up, your emails may end up in spam – or not be delivered at all.
Postal requires several DNS records to function properly. Let’s walk through each type.
A & AAAA
We’ll begin by creating two essential DNS records: an A record and an AAAA record:
Record Type | Host | Value (IP Address) |
---|---|---|
A | mailout.example.com | Primary IPv4 |
AAAA | mailout.example.com | Primary IPv6 |
Then, add A and AAAA records for the MX hostname that we will use later for receiving emails through routes:
Record Type | Host | Value (IP Address) |
---|---|---|
A | mx.mailout.example.com | Primary IPv4 |
AAAA | mx.mailout.example.com | Primary IPv6 |
All records should point to your server’s primary IPs
PTR (Reverse DNS)
PTR (Reverse DNS) records are used by receiving mail servers to verify that your server’s IP address maps back to its hostname – this is a key check for spam prevention and essential for passing many email reputation filters.
Now, set up reverse DNS (PTR) for both IPv4 and IPv6:
- Go to Hetzner dashboard → Server Details → Networking tab
- Find the IP you want to edit, click the three dots → Edit Reverse DNS
- Set the reverse DNS to match your hostname (
mailout.example.com
) - For IPv6, append
::1
and update the hostname accordingly
SPF Record
It's crucial to add an SPF (Sender Policy Framework) record to your DNS setup.
This record helps prevent your emails from being marked as spam by verifying that your server is authorized to send emails on behalf of your domain.
Add a global SPF record that includes your primary IPs:
Record Type | Host | Value |
---|---|---|
TXT | spf.mailout.example.com | "v=spf1 ip4:70.130.219.212 ip6:643c:ac1f:3f7c:bb5c::1 ~all" |
This is a global SPF record you will use for any domain you add to Postal.
Return Path (MX + SPF + DKIM)
The Return Path is the email address that gets used to handle bounces (rejected or undelivered emails) for any email you send.
It tells the receiving mail server where to send error messages if it can't deliver an email (like if the recipient's email address doesn't exist, or the mailbox is full).
It's different from the From address and is usually hidden from end users – but it’s essential for proper bounce handling.
Think of it like this:
- You send an email to someone.
- If the email can't be delivered, a bounce email will be sent back to the return path address, not your From address.
To configure a return path domain in Postal, we need to add three DNS records:
Record Type | Host | Mail Server | Priority |
---|---|---|---|
MX | rp.mailout.example.com | mailout.example.com | 10 |
This tells the world where to send bounce emails for the return path.
It means bounces for emails sent from mailout.example.com
will go to rp.mailout.example.com
(your return path domain), which will then point to your Postal server to handle them
Record Type | Host | Value |
---|---|---|
TXT | rp.mailout.example.com | "v=spf1 a mx include:spf.mailout.example.com ~all" |
This authorizes your return path domain to send emails and helps prevent bounce messages from being flagged as spam.
The last DNS record we need to add is the DKIM (DomainKeys Identified Mail) record. It is an email authentication method that uses a digital signature to verify that the email was sent and authorized by the domain owner.
Postal provides a command that generates a DKIM record for us, which begins with the postal
DKIM identifier. If you'd like to customize this identifier, open the /opt/postal/config/postal.yml
file and add the following to the dns:
section:
dns:
dkim_identifier: custom
Afterward, run this command to generate the DKIM record:
sudo postal default-dkim-record
In my case, I am adding a TXT record with the host value of:
custom._domainkey.rp.mailout.example.com
The value for this record will be the output generated by the command.
Route Domain
If you wish to receive incoming emails by forwarding them directly to routes in Postal, you'll need to configure a domain and point it to your server using an MX record, like this:
Record Type | Host | Mail Server | Priority |
---|---|---|---|
MX | routes.mailout.example.com | mailout.example.com | 10 |
This lets incoming emails sent to routes.mailout.example.com
reach Postal for routing.
DMARC
DMARC (Domain-based Message Authentication, Reporting, and Conformance) is an email authentication protocol that helps prevent email spoofing and phishing by allowing domain owners to specify how incoming emails should be handled if it fails SPF or DKIM checks.
The DMARC record specifies:
- Whether to accept, quarantine, or reject emails that fail authentication.
- How the domain owner wants to receive reports about these emails.
For this, add a TXT record with a host value starting with _dmarc
for your domain and containing the following:
"v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com"
p=quarantine
: This policy tells receiving mail servers to treat emails that fail DMARC checks as suspicious and place them in the recipient's spam or junk folder.rua=mailto:postmaster@example.com
: This specifies the email address where aggregate reports about DMARC failures will be sent, allowing the domain owner to monitor and analyze the performance of their DMARC policy.
Adding a DMARC record improves both your domain's security and email deliverability.
Verify DNS Configuration
If you open the /opt/postal/config/postal.yml
file, you will see DNS records specified under the dns:
section.
This section essentially tells Postal which DNS records you’ve added and how Postal should recognize them.
By default, it includes all the necessary records – except for the Track Domain, which is used for click and open tracking. Since that’s outside the scope of this guide, you can safely comment it out.
Postal also supports a couple of optional DNS-related settings you can customize in the same file:
dns:
domain_verify_prefix: custom-verification
custom_return_path_prefix: customrp
By default, domain_verify_prefix
is set to postal-verification
and custom_return_path_prefix
to psrp
. You can personalize these values to reflect your domain or brand.
Starting Postal
With everything configured, it’s time to bring Postal to life.
First, initialize the database and create your admin user:
sudo postal initialize
sudo postal make-user
Once that’s done, start Postal:
sudo postal start
This command launches all the necessary components using Docker containers.
You can verify that everything is running smoothly by checking the service status:
sudo postal status
If you run the sudo docker ps
command, you’ll notice several new containers up and running – these are the components that power your Postal installation.
Web Access with Caddy
To access Postal’s web interface, you’ll need to set up a web proxy.
You can use any reverse proxy you're comfortable with, but in this guide, we’ll use Caddy.
It is a lightweight, modern web server that’s easy to configure and automatically handles SSL certificates via Let's Encrypt.
Run the following command to start the Caddy container:
sudo docker run -d \
--name postal-caddy \
--restart always \
--network host \
-v /opt/postal/config/Caddyfile:/etc/caddy/Caddyfile \
-v /opt/postal/caddy-data:/data \
caddy
Once it’s running, Caddy will automatically request and install an SSL certificate for your domain.
You should now be able to access the Postal web interface using your hostname and log in with the admin user you created earlier.
Securing the Web Interface
Someone asked about having a kind of 2FA for the web interface, since Postal doesn't provide any 2FA feature for now, and brought up a really good point.
You can restrict access to ports 80 and 443 to a static IP (like a VPN), but this doesn't work for everyone.
So, to add an extra layer of security to the normal username and password authentication method that Postal provides, we can enable Basic Authentication through Caddy, which is easy to configure.
Inside the /opt/postal/config/
directory, you'll find the Caddyfile
file. Open it and add the following, so the configuration looks like this:
mailout.examle.com {
reverse_proxy 127.0.0.1:5000
basicauth {
username $2a$14$dp6WI0ldDiY/lFUL5I7q7ug/29DbwHYe5oFO6Z6mN6SRDIz/1/lgK
}
}
Replace username
with the username you want to use, and what follows the username is the password, but hashed using bcrypt, which is what Caddy uses.
While the caddy hash-password
command works normally, it won’t work in this case since we run Caddy in a container, and the command is not available. As a quick alternative, you can use this online tool to hash your password.
After generating the hash, replace the hash in the example above with your own, then save and close the file.
Now, run the following two commands to stop and start Caddy again:
sudo docker stop postal-caddy
sudo docker start postal-caddy
Finally, try to access the web interface, and you should not be able to do so unless you enter the username and password you just added to the Caddyfile
file.
Time to Mail It!
With Postal installed and running, it’s time to test your setup.
Start by creating an organization. Give it a name and click Create Organization.
Once inside:
- Go to the Settings tab
- Update the timezone to match your local time (this keeps log timestamps accurate)
Simple but important.
A Quick Note on Domains
Before adding your first mail server, there's something important to note.
You can add a domain:
- At the organization level: applies to all mail servers in that organization.
- Inside a specific mail server: applies only to that one server.
DNS can get complicated and busy when the domain is linked to all mail servers created within the organization. This is something I was advised by a member of the Postal team on GitHub.
If that ever changes and I find a good use case for organization-level domains, I’ll update this guide. But for now, let’s keep it tidy.
Create a Mail Server
Go ahead and create your first mail server. When you do:
- Set the Mode to
Live
- Once it’s created, head to the Domains tab
- Add your sending domain (usually the same one you used to set up Postal)
I always add my main domain here and set up a route for postmaster@example.com
– that way, I can receive DMARC reports as mentioned earlier in the DNS section.
Inside your mail server, go to Settings → Server Settings, and you’ll find a field called POSTMASTER:
- This is the contact address that shows up in bounce messages if someone sends email to a nonexistent address.
- It defaults to
postmaster@example.com
, and that’s exactly what most servers expect – so I recommend keeping it as is.
For example, if someone tries to email an address you haven’t set a route for, it will bounce, and the bounce message will mention the POSTMASTER address as the contact point.
That brings us to the MX record:
- If you're not using Postal to receive mail: you can skip adding the MX record Postal suggests when adding a new domain.
- If you're not using another service for incoming mail: I still recommend adding the MX record.
It helps make your sending email address appear more trustworthy and legitimate.
Send a Test Email
Let’s make sure it actually works.
- Go to the Messages tab → Send Message
- In the FROM field, use something like
hi@example.com
(make sure that domain has been added) - For the TO field, use a spam-checking tool like mail-tester.com
- It’ll give you a temporary email address
- Paste that into the TO field and click Send Message
Then head back to the test tool and check your score.
Configuring Routes
It's time to set up routes so you can receive incoming mail.
Inside your mail server, go to the Routing tab. You’ll see three endpoint types available:
- HTTP
- SMTP
- Address
For now, we’ll focus on the Address endpoint – it’s the simplest and most practical option for forwarding emails to your inbox.
Create an address endpoint with the email address you'd like incoming emails forwarded to – this can be your personal or business inbox.
Create a Route
Now go to the Routes sub-tab and click Add your first route.
Fill in the following fields:
- Name: The email address you want to receive emails at (e.g.
postmaster
). If you've already added your domain, just select it from the dropdown. - Endpoint: Select the address endpoint you just created.
- Spam Mode: Choose your preferred option (we’ll configure spam filtering later).
- Click Create Route.
postmaster@yourdomain.com
is useful for receiving DMARC reports and bounce notifications.Test Your Route
Try sending an email to the address you configured – e.g. postmaster@example.com
and check if it lands in the inbox of the address you configured as the endpoint.
You can also view incoming mail in Postal:
- Go to the Messages tab → Incoming Messages
If you open a mail, you’ll first see a summary – like whether it's marked as spam.
A Quick Security Check
Now head to the Outgoing Messages tab and open any mail you’ve sent.
In the Properties tab, you’ll see delivery info – like whether it was sent over a secure (SSL) connection.
But here's the catch: incoming mail don’t show this same detail.
We’ll fix that next.
SMTP TLS
By default, Postal does not have TLS enabled for incoming mail, which means your server isn’t currently receiving emails securely.
You can confirm this using the CheckTLS tool. Just enter your domain in the eMail Target field and click the Run Test button.
The test should return an error like:
TLS is not an option on this server
You can also use this command to confirm that no SSL is configured and TLS isn’t working:
openssl s_client -connect mailout.example.com:25 -starttls smtp
This command connects to your mail server on port 25 and tries to upgrade the connection to a secure one using TLS.
If TLS isn’t enabled yet, you’ll see an error like:
Didn't find STARTTLS in server response, trying anyway...
This output means your server isn’t advertising TLS as an option, so incoming connections can’t be encrypted – exactly what we’re trying to fix next.
Why This Happens
I spent three days debugging this exact issue, only to realize it was something simple – and poorly documented in Postal’s official docs.
Here’s what you need to know:
- When sending email, Postal doesn’t need an SSL certificate – the receiving server handles encryption.
- But when receiving email, your Postal server is the receiving server – so you must provide a certificate to support TLS.
When using routes to receive emails, Postal first receives the message, then forwards it to the address endpoint you specified. This forwarding process is encrypted using an SSL certificate, because – as mentioned above – during forwarding, Postal acts as the sending server, and the receiving server (e.g. Gmail) handles the encryption. However, the initial connection between the original sender and Postal is not secure unless SMTP TLS is enabled.
Also important: without TLS, apps like WordPress won’t be able to submit email to Postal securely – so enabling it is strongly recommended, even if you're not receiving mail directly. Because WordPress first submits the email to Postal, which then forwards it to the final recipient.
Issue an SSL Certificate
We need an SSL certificate for both the hostname and the MX record. To obtain one, we’ll use Certbot.
Start by installing it:
sudo apt install certbot
When issuing a certificate, Certbot needs to use port 80 for domain verification. However, that port is currently in use by the Caddy web server, which is handling web traffic for Postal's web interface.
To work around this, we’ll temporarily stop the Caddy container, issue the certificate, and then start the container again – all in one command:
sudo docker stop postal-caddy && sudo certbot certonly --standalone -d mailout.example.com -d mx.mailout.example.com --agree-tos --email postmaster@example.com --non-interactive && sudo docker start postal-caddy
Once that completes, your certificate will be successfully issued.
sudo certbot certificates
command.Configure Postal to Use TLS
Now that we have our SSL certificate, we need to copy it to the /opt/postal/config/
directory and rename its certificate and key files for Postal to use:
sudo cp /etc/letsencrypt/live/mailout.example.com/fullchain.pem /opt/postal/config/smtp.crt
sudo cp /etc/letsencrypt/live/mailout.example.com/privkey.pem /opt/postal/config/smtp.key
Next, change the file permissions for the certificate files:
sudo chmod 644 /opt/postal/config/smtp.*
Then, open the /opt/postal/config/postal.yml
file and add the following:
smtp_server:
tls_enabled: true
tls_certificate_path: /config/smtp.crt
tls_private_key_path: /config/smtp.key
Finally, restart Postal to apply the changes:
sudo postal restart
To verify that SMTP TLS is working, run this command again:
openssl s_client -connect mailout.example.com:25 -starttls smtp
You should now see a successful TLS connection.
Next, run the CheckTLS test again by entering your domain as the email target and clicking Run Test. Check the results to confirm that TLS is properly enabled and functioning.
Handle Auto-Renewal with Certbot
Since Certbot uses port 80 and Caddy is running, the auto-renewal will fail unless you automate stopping/starting Caddy.
If you're curious, the configuration for auto-renewal can be found inside the /etc/letsencrypt/renewal
directory.
Certbot handles auto-renewals using systemd timers instead of cron jobs. This timer run twice per day and can be checked using the following command:
sudo systemctl list-timers
You can examine the content of the timer with the following command:
sudo cat /lib/systemd/system/certbot.timer
Now, to solve this problem, we can use something called Renewal Hooks.
Inside the /etc/letsencrypt/renewal-hooks
directory, you’ll find three different subdirectories:
deploy
: This directory is for scripts that run after a certificate is successfully renewed.post
: Scripts in this directory run after the certificate is renewed and deployed.pre
: These are scripts that run before Certbot tries to renew the certificate.
pre
to stop the Caddy container, deploy
to copy the renewed certificate files, change their permissions, and restart Postal, and post
for starting the Caddy container again.Inside the pre
directory, create a script called stop_caddy.sh
with the following content:
#!/bin/bash
docker stop postal-caddy
Inside the deploy
directory, create a script called copy_ssl_cert.sh
with the following content:
#!/bin/bash
# Copy new certificate files
cp /etc/letsencrypt/live/mailout.example.com/fullchain.pem /opt/postal/config/smtp.crt
cp /etc/letsencrypt/live/mailout.example.com/privkey.pem /opt/postal/config/smtp.key
# Set correct permissions
chmod 644 /opt/postal/config/smtp.*
# Restart Postal
postal restart
Finally, inside the post
directory, create a script called start_caddy.sh
with the following content:
#!/bin/bash
docker start postal-caddy
You can test if everything is working by first checking the expiry date using the following command:
sudo certbot certificates
This should match the expiry date of the copied certificate. To check the expiry date of the certificate in Postal, run:
openssl x509 -enddate -noout -in /opt/postal/config/smtp.crt
Then, force the renewal using the following command:
sudo certbot renew --force-renewal
Lastly, check the expiry date again to make sure it matches. If it does, the setup is working correctly.
Installing SpamAssassin
For filtering spam emails, we will install and use SpamAssassin, a powerful and widely-used spam filtering tool.
By default, Postal will communicate with SpamAssassin's spamd
service using a TCP socket connection (port 783).
You’ll need to install SpamAssassin on your server and enable it within Postal to begin filtering spam.
For installing SpamAssassin, use the following command:
sudo apt install spamassassin
Next, you will need to enable the SpamAssassin timer:
sudo systemctl enable --now spamassassin-maintenance.timer
It is used to update the spam rules automatically.
To enable spam checking, we need to add the following to the end of the /opt/postal/config/postal.yml
file:
spamd:
enabled: true
host: 127.0.0.1
port: 783
Finally, restart Postal to apply your changes:
sudo postal restart
Inside Postal web interface:
- Go to your Organization Settings → Spam tab
- You’ll see the Spam Threshold setting (default: 5)
A score above the threshold triggers the SPAM MODE set in your route config. A score below the threshold means the message is treated as non-spam.
There’s also a Spam Failure Threshold (default: 20). If a mail scores above 20, it is immediately dropped.
What Do Spam Scores Mean?
Now, let's talk about what spam scores are before we enable spam checking for outgoing emails.
SpamAssassin works by analyzing an email and giving it a spam score.
- The lower the score, the better the chances of the email getting delivered successfully.
- Conversely, the higher the score, the higher the likelihood the message is labeled spam/junk.
- A score below 5 is considered decent, while anything above 5 indicates a higher chance of being filtered out.
SpamAssassin uses over 700 tests and a variety of analytical techniques to detect spam. Each attribute it checks contributes to the overall score.
Negative scores indicate the email is less likely to be spam, while positive scores suggest possible spam.
When setting up SpamAssassin, the score threshold can be adjusted, with 5 being the default.
To ensure successful email delivery, aim for a spam score below 5, with scores between 0-2 being optimal. Negative scores are even better, as they indicate the email is very unlikely to be marked as spam, though they can be difficult to achieve.
I recommend starting with a threshold of 5 and gradually lowering it based on your spam checking history. For instance, if you notice your outgoing emails consistently have a score of 3, consider lowering your threshold to 4.
Over time, work on improving your score and address the factors contributing to higher scores.
Enable Spam Checking for Outgoing Mail
To filter outgoing mail:
- Go to your mail server Settings → Advanced Settings
- Find the Outbound Spam Threshold
- Set it to
5
- Click Save Server
Now outgoing mail will also be checked using SpamAssassin before it’s sent out.
System Email Configuration
To enable system-generated emails (such as password reset links) in Postal, you'll need to configure the smtp:
section in the Postal configuration file.
Open the /opt/postal/config/postal.yml
file and scroll to the bottom of it, and you’ll find the smtp:
section. This is where you'll define the SMTP server Postal should use for sending system emails.
Although you could use an external SMTP provider, we’re going to use Postal itself to keep everything self-hosted.
Inside your Postal web interface:
- Go to your Mail Server
- Click the Credentials tab
- Click Add Credential
- Leave the TYPE set to SMTP
- Name it something like
System
to keep things organized
- Click Create Credential – a password will be generated automatically
Now go to the Help tab → Sending Email sub-tab. You’ll see the SMTP settings needed to complete the smtp:
section in your postal.yml
file.
Back in /opt/postal/config/postal.yml
, fill out the smtp:
section with the info you just retrieved. For the from_address
, it's a good idea to use something like system@example.com
.
Make sure to also change the port from 2525
to 25
, since our SMTP server uses port 25 for sending emails.
Finally, restart Postal to apply your changes:
sudo postal restart
Now, try logging out of the Postal web interface and resetting your password. You should receive an email with a link to set a new one.
Enabling UFW Firewall
We finally want to enable the UFW firewall on our server, and I've left this to the end so you can check if everything still works perfectly after enabling it.
We only need to allow incoming connections, as outgoing traffic is allowed by default with UFW.
To configure this, we need to allow incoming traffic on the following ports: 22 (SSH), 25 (SMTP), 80 (HTTP), and 443 (HTTPS).
Use the following commands to allow these connections and enable the firewall:
sudo ufw limit 22/tcp
sudo ufw allow 25/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Sometimes, UFW may require a reboot to work correctly. So, if you encounter any issues, try rebooting the server.
Disaster Recovery
The final step in setting up your SMTP server is planning for disaster recovery. Think about what you’d do if something goes wrong – like a server breach, a misconfiguration, a bad update, or even something out of your control, like a fire in your provider’s data center. You need a way to quickly bring everything back online.
We already have our primary IPs secured – they stay with us no matter what, which is great. Now, we need a reliable way to restore the server and assign those same IPs to it. The best way to do this is by using snapshots.
In Hetzner, you can go to your server’s Snapshots tab and create a snapshot, which is basically a copy of your server's disk at that moment.
If something happens, you can spin up a new server from that snapshot, assign the primary IPs, and it should work immediately – no need to change any DNS settings.
Make sure to test this process a few times so you’re comfortable with it.
Snapshots are created manually. If you want automatic backups, you can enable Hetzner's backup feature. It costs 20% extra, but it keeps daily backups for a week. Once the week is over, old backups are replaced by new ones. You can convert any of these backups into a snapshot, which you can then use to restore your server.
Conclusion and Final Thoughts
Congratulations on reaching the end!
Remember to monitor your setup, fine-tune your spam settings, and keep your SSL certificate renewed.
And just as important – test your disaster recovery plan regularly to make sure everything still works as expected. It’s better to catch issues during a test than during a real outage.
If you run into any issues or need further help, feel free to revisit this guide or reach out for assistance. Happy emailing!
If you found value in this guide or have any questions or feedback, please don't hesitate to share your thoughts in the discussion section.
Discussion