Altoplace

Create a Send-Only SMTP Server on macOS

Computers sending email.
Image by mohamed Hassan from Pixabay .

This post describes how I configured Postfix on my Mac to relay outbound email via my Pair Networks SMTP service.

Why? I am creating a local website development environment where PHP applications may generate email messages for contact forms or notifications. For example, after installing WordPress, it generates an email message announcing a successful install. However, I can use the built-in mail command to run a simple test to verify that my send-only mail service is working.

I am using macOS Sonoma on my Mac. Postfix is already installed and ready to go. I only need to configure it to use my SMTP service. You will need to have an existing mail account that allows access to the out-going SMTP service. Setting up Postfix to relay locally originated email through your mail account is much easier than trying to build a full-fledge email server on your Mac. And most ISPs will not allow you to run an email server on your local computer. If this all sounds good, then read on.

This post was originally written describing how to use Gmail as an SMTP host, using an Application password. I am no longer using Gmail as an SMTP host. What I am now describing (and have tested) is how to use any SMTP service that allows external access. From my experience, mail servers may be tricky. I have had a couple of comments about not being able to get this procedure to work. I have carefully gone through this procedure, testing with my Pair Networks server. Please let me know if you run into any issues.

macOS Postfix SMTP Configuration

First, you need to add the following lines to /etc/postfix/main.cf using your favorite editor and sudo:

$ cd /etc/postfix

$ sudo vi main.cf
Password:

$ cat main.cf
 o o o
# Setup up send-only SMTP server (inet_interfaces may already be set)
inet_interfaces = loopback-only

# Define the relay host.
relayhost = [user.mail.pairserver.com]:587

# Map local email (`whoami`@`hostname -f`) to a real email address.
#   After creating the 'generic' file, do:
#   sudo postmap /etc/postfix/generic
smtp_generic_maps = hash:/etc/postfix/generic

# Use TLS/SASL authentication
smtp_sasl_auth_enable = yes

# After creating the 'sasl/passwd' file, do:
# sudo postmap /etc/postfix/sasl/passwd
smtp_sasl_password_maps = hash:/etc/postfix/sasl/passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_mechanism_filter = plain
smtp_use_tls = yes
smtp_tls_security_level = encrypt
The relay host is your external SMTP server where you have one or more email addresses hosted. (user.mail.pairserver.com is the naming convention at Pair Networks; be sure to use the correct SMTP host name for your SMTP service). My SMTP host is using port 587 for secure access.

I need to create a couple of support files. The first one, generic, will create a mapping from my local machine account name to an email address hosted by my SMTP host. The second one, sasl/passwd, contains my SMTP email account password in a machine readable format.

The generic mapping file should exist already. Add lines similar (but use your actual account names) to the following at the end of the file and run postmap to generate the machine readable file, generic.db:

$ echo `whoami`@`hostname -f`
$ cd /etc/postfix

$ mkdir -p ~/orig/etc/postfix
$ cp generic ~/orig/etc/postfix
$ sudo vi generic

$ cat generic
 o o o
# echo `whoami`@`hostname -f`    user@mymail.com
macuser@mymac user@mymail.com
@mymac        user@mymail.com

$ sudo postmap /etc/postfix/generic

ls generic*
generic    generic.db

You can execute:

 $ echo `whoami`@`hostname -f`

to determine what the first mapping should be. This gives you the user at the Mac hostname. Be sure to use the FQDN hostname (the hostname -f output). The second mapping in the generic file is a catchall for any user on your machine. The user@mymail.com address is an email address on your SMTP host. You can tweak this file as desired.

Next, I need to create the hashed passwd file. It can go anywhere; I decided to create a subdirectory, sasl, to store it in. After I create the file, I will again use postmap to create the machine readable version:

$ sudo mkdir -p /etc/postfix/sasl

$ cd /etc/postfix/sasl

$ sudo vi passwd

$ cat passwd
[user.mail.pairserver.com]:587    user@mymail.com:smtp-password

$ sudo postmap passwd

$ ls passwd*
passwd    passwd.db

$ sudo chmod 600 passwd*

$ ls -l passwd*
-rw------- 1 root  wheel     61 May 15 14:22 passwd
-rw------- 1 root  wheel  16384 May 15 14:28 passwd.db

The passwd file is mapping your SMTP host and port number to an email address and password that is hosted by your SMTP service. This is assuming that you use an email address to log into your email host. It should be the same email address that you used in your generic file.

Testing The SMTP Relay Service

And that’s all you have to do! The postfix service is automatically loaded by launchctl when your Mac starts up. The postfix service is not running, however, it’s configured to run when it receives a request. If you are interested, have a look at:

$ cat /System/Library/LaunchDaemons/com.apple.postfix.master.plist

To test your new postfix configuration, first run this log command in a separate terminal window:

$ log stream --predicate 'process == "smtp"' --info
Filtering the log data using "process == "smtp""

Now, run this mail command in a separate terminal window to send mail to an external email address:

$ echo "This is some email." | mail -s "This is the email subject" user@gmail.com

You should see the email at your receiving email address. You should also see output in the log terminal window that looks like:

 ooo
 ooo
 ooo
2024-02-28 14:11:22.963291-0600 0x17d6e9   Info        0x0                  41893  0    smtp: 557EC16A21AD: to=<user@gmail.com>, relay=user.mail.pairserver.com[216.146.195.69]:587, delay=1.3, delays=0.66/0.03/0.45/0.13, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as D3642F1B8D)

There are several lines of output about the smtp process. Look for the last line that shows the status as sent.

While this does work, I like having the postfix service running all the time. It’s helpful for debugging and checking the postfix status. Some examples for using the postfix command:

$ sudo postfix status
postfix/postfix-script: the Postfix mail system is not running

$ sudo postfix start 
postfix/postfix-script: starting the Postfix mail system

$ sudo postfix status
postfix/postfix-script: the Postfix mail system is running: PID: 18100

# After making any changes to the postfix configuration files ...
$ sudo postfix reload
postfix/postfix-script: refreshing the Postfix mail system

$ mailq
Mail queue is empty

That last command, mailq, can be used for debugging purposes. For example, if an email has not been received, you can quickly check to see if it’s still queued up on your local machine.

Final Thoughts

I have created a simple solution for testing Apps that need to generate email, such as a contact form in WordPress. This is a great solution for low-volume email when testing website Apps. So far it seems to be working well for me. As always, I will try to keep this post up to date with any additional tweaks that I make.