Cloudflare’s email-routing feature is useful. I had also used SendGrid for sending mail and was looking for alternatives. Self-hosted mail options include Mail-in-a-Box, Docker Mailserver, and Mailcow. Sometimes I do not need an inbox; I need reliable transactional delivery and incoming-mail routing. I therefore tried Postal as a self-hosted alternative to platforms such as SendGrid, Mailgun, and Postmark. This article records a single-server Postal v3 installation.

Preparation

A Domain Name

Usually, a subdomain is sufficient, such as postal.example.com. This will be used for management and configuration purposes. Once installed, you can bind new domains to send and receive emails.

A VPS

It is best to have a VPS dedicated to Postal, with inbound and outbound TCP port 25 allowed and a stable public IPv4 address (IPv6 is useful too). Set rDNS for the sending IP so it agrees with the SMTP HELO hostname and forward A/AAAA records. These are the basics of delivery reputation, not a guarantee of inbox placement. Postal also recommends a dedicated server and warns that many VPS providers block outbound port 251.

How to Test If Your VPS Allows Traffic on Port 25

Test Connection to Other Servers on Port 25
nc -vz aspmx.l.google.com 25
Test If External Servers Can Connect to Your VPS on Port 25

Listen on port 25 on your VPS:

nc -l 25

Allow inbound traffic on port 25, for example, using UFW:

ufw allow 25/tcp

Connect to your local port 25 from an external server:

nc -vz <YOUR_VPS_IP> 25

Luckily, the ServerHub Dedicated Hosting I’m using doesn’t have a port 25 restriction. At the same time, the DMIT I’m using doesn’t allow IPv4 outbound traffic on port 25. But it allows inbound traffic on port 25, which should probably be credited to Tao Shu 2.

Install Postal

Postal recommends at least 4GB RAM, two CPU cores, and 25GB of storage. It runs in containers, so Ubuntu 22.04 or 24.04 will work; the important requirement is a working Docker Engine and Compose plugin, not a particular distribution release1.

apt install git curl jq
git clone https://github.com/postalserver/install /opt/postal/install
sudo ln -s /opt/postal/install/bin/postal /usr/bin/postal

Docker

Postal does not officially support Podman, so we should install Docker:

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

MariaDB

You can use Docker.

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

Alternatively, you can run MariaDB without Docker. Ensure that Postal has all privileges on the postal and postal-% databases.

GRANT ALL PRIVILEGES ON `postal`.* TO 'postal'@'%' IDENTIFIED BY 'YOUR_PASSWORD' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON `postal\-%`.* TO 'postal'@'%' IDENTIFIED BY 'YOUR_PASSWORD' WITH GRANT OPTION;
FLUSH PRIVILEGES;

To log emails, confirm that innodb_log_file_size is ten times the maximum email size. Check the current configuration 3:

SHOW GLOBAL VARIABLES LIKE 'innodb_log_file_size';

Setting Up Postal

Run:

postal bootstrap postal.yourdomain.com

This command creates postal.yml, signing.key, and Caddyfile in /opt/postal/config. Edit postal.yml to set database credentials and DNS domains. The container mount path is /config, so absolute paths in the configuration should use /config/..., not the host path /opt/postal/config/...4:

vim /opt/postal/config/postal.yml

ghcr.io

Visit https://github.com/settings/tokens/new to generate a classic token5

export CR_PAT=YOUR_TOKEN
export ghcr_user=YOUR_GITHUB_USERNAME
echo $CR_PAT | docker login ghcr.io -u $ghcr_user -password-stdin

Initialize the Database and More

postal initialize

Create an Admin User

postal make-user

Start Postal

postal start

Caddy

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

Configuring DNS Records

Create A and AAAA records for postal.example.com pointing to your server’s IP. Follow https://docs.postalserver.io/getting-started/dns-configuration to configure other DNS records.

In the configuration file, the domains in dns.mx_records should all be CNAMEs to postal.example.com.

Using Postal

Access postal.example.com, create an organization, then create a mail server, add a domain, configure the DNS records for the mail domain as prompted on the page, and verify them.

Go to Messages -> Send Message to send a test email. Visit https://www.mail-tester.com to get a test email address and send a test email to see if you can achieve a perfect score.

In Credentials, you can add SMTP credentials to start sending emails.

It’s recommended to enable privacy mode in the settings to avoid leaking the IP addresses of SMTP clients.

Conclusion

Postal also supports email receiving features similar to Cloudflare’s email routing, allowing forwarding to mailboxes or HTTP endpoints. Recently, my SendGrid account became inaccessible for unknown reasons. After using Postal for a while, I found it can completely replace services like SendGrid and offers more freedom.


References

  1. Postal. Pre-requisites. Accessed 2026-09-04. 2

  2. 涛叔. 记录开通 25 号端口的经历. Taoshu.in. 2022.

  3. https://mariadb.com/docs/server/storage-engines/innodb/operations/configure-redo-log/

  4. Postal. Installation. Accessed 2026-09-04.

  5. Andrew Hoog. Authorizing GitHub Container Registry. DON’T PANIC. 2023

Affiliate disclosure and original links

Thank you for purchasing through links marked [Aff]; the author may receive benefits.

DMITwww.dmit.io