Setting up Postfix with external SMTP
Having trouble getting mail to send with Postfix? Learn how to configure Postfix with an external SMTP server.
Getting a new server up and running isn't quite complete until you can send mail for purposes of monitoring. I recently setup a new server this week and configured unattended upgrades but I wanted a way to be notified in case of errors or in the event I needed to login to manually perform an action (reboot after a kernel upgrade, for example). I have used Postfix in the past but always ran into issues getting it to actually work.
Installing Postfix
First, we need to make sure we have Postfix installed on our server:
Debian/Ubuntu-based:
$ sudo apt install postfix
RHEL/Fedora-based:
sudo dnf install postfix
Arch-based:
sudo pacman -S postfix
During the process, it's going to ask you a series of questions. Since we are setting this up to be used with an external SMTP server we will select "Internet Site":
![Postfix Install - Step 1](/img/posts/2022/01/21/postfix-step1.jpg "Select "Internet Only"")
On the next screen, it's going to ask you for your "System mail name". This is the domain used in the email address(es) you'll be using. In my case, that's gradiian.io
:
You can verify Postfix is installed and running:
$ sudo systemctl status postfix
● postfix.service - Postfix Mail Transport Agent
Loaded: loaded (/lib/systemd/system/postfix.service; enabled; vendor preset: enabled)
Active: active (exited) since Fri 2022-01-21 15:44:57 UTC; 1h 35min ago
Process: 51586 ExecReload=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 4378 (code=exited, status=0/SUCCESS)
Here we can see Postfix is running and is also set to start on boot because it shows enabled
. If for some reason it shows disabled
instead, you can make it start on boot by enabling it:
$ sudo systemctl enable postfix
Configuring Postfix
After the installation completes, we now need to configure Postfix. First, let's start by creating the sasl_passwd
file which will contain our credentials for our SMTP server.
$ sudo vim /etc/postfix/sasl_passwd
Inside this file, we'll include three pieces of information:
-
SMTP server address
-
SMTP username
-
SMTP password
You'll want to check with your mail provider to determine what your username is. For some hosts, this username is just the email address itself. If you're using some kind of two-factor authentication, you may need to create an app password to be able to login.
I use Namecheap as my mail host so that's what I'll be using in the following examples. If you use Namecheap, your username will be the full email address of the user you created when setting up the mailbox:
mail.privateemail.com [email protected]:password
Next, we need to set the permissions on this file so that no one else can read it:
$ sudo chmod 600 /etc/postfix/sasl_passwd
For those who don't know,
chmod 600
allows "read/write" (6) access to owner and no privileges to group (0) and other (0).
Now, we create a hashed database of this file which Postfix will use. The resulting file will be the same filename with an added .db extension.
$ sudo postmap /etc/postfix/sasl_passwd
$ ls -l /etc/postfix/sasl*
-rw------- 1 root root 69 Jan 21 16:12 /etc/postfix/sasl_passwd
-rw------- 1 root root 12288 Jan 21 16:12 /etc/postfix/sasl_passwd.db
Now, let's open the main Postfix configuration file:
$ sudo vim /etc/postfix/main.cf
In this file, we need to change a few existing options. First, let's set myhostname
to the domain in our email:
myhostname = gradiian.io
Next, we need to remove our domain from the mydestination
list if it's there otherwise mail will be sent locally on the server rather than through our SMTP server:
mydestination = localhost.localdomain, localhost
Lastly, we need to specify our relayhost
which is going to be set to our SMTP server and port:
relayhost = mail.privateemail.com:587
At the bottom of main.cf we will add the following options to properly authenticate with our SMTP server:
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_use_tls = yes
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
Save and close the file. We'll now reload Postfix and try and send an email:
$ sudo systemctl reload postfix
Let's make sure mail
is installed so we can send an email:
$ which mail
$
If you don't see the path to mail returned on the next line, you don't have mail installed. You can get this by installing the mailutils
package:
$ sudo apt install mailutils
Let's try that again:
$ which mail
/usr/bin/mail
Perfect! The mail
program has been installed and now we can test out our Postfix configuration.
Pro Tip: I found it much easier to troubleshoot my Postfix configuration by tailing the mail log in a separate window so I could watch the output of the logs for errors in real time:
$ sudo tail -f /var/log/mail.log
To send a test email, we can use the previoulsy installed mail
command to send a message. Replace [email protected]
in the example below with the email address you wish to send the test email to:
$ echo "This is a test message" | mail -s "My clever subject" [email protected]
Let's check our mail log to see if that message was sent. If you aren't using tail
to watch the output of the log, you can open it in a text editor and check the lines nearest the bottom:
$ vim /var/log/mail.log
Jan 21 16:49:09 localhost postfix/smtp[29150]: A0A04429E1: to=[email protected], relay=mail.privateemail.com[198.54.122.135]:587, delay=1.3, delays=0.04/0.01/0.87/0.4, dsn=4.1.8, status=deferred (host mail.privateemail.com[198.54.122.135] said: 450 4.1.8 user@localhost: Sender address rejected: Domain not found (in reply to RCPT TO command))
Notice the last bit: said: 450 4.1.8 user@localhost: Sender address rejected: Domain not found. The server rejected our email because the sender address of user@localhost is invalid. We need to specify a From address that matches an existing email address on our mail host.
Rather than specifying a From address each time we want to send mail, we'll use Postfix's generic maps to rewrite our local address to our actual address.
$ sudo vim /etc/postfix/generic
Inside this file we'll not only map our current user but also for root which will allow mail from the system. Replace 'localhost' in this example with the value you set in main.cf
for myhostname
:
user@localhost [email protected]
root@localhost [email protected]
Now, when Postfix gets mail to send, it will map user@localhost to [email protected]. We need to generate a hash of this file as well:
$ sudo postmap /etc/postfix/generic
...and then we'll add it to the bottom of the Postfix configuration, /etc/postfix/main.cf:
smtp_generic_maps = hash:/etc/postfix/generic
Save and close the file and then reload Postfix:
$ sudo systemctl reload postfix
Now, go ahead and send an email again using the same command we used earlier. This time, the email should go through which you can verify in mail.log:
Jan 21 17:16:56 localhost postfix/qmgr[51600]: DAC79429E3: from=<user@localhost>, size=349, nrcpt=1 (queue active)
Jan 21 17:16:57 localhost postfix/smtp[56519]: DAC79429E3: to=<[email protected]>, relay=mail.privateemail.com[198.54.122.135]:587, delay=392, delays=391/0.02/0.87/0.21, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 2961C18000A2)
Jan 21 17:16:57 localhost postfix/qmgr[51600]: DAC79429E3: removed
Notice status=sent
on the second line as well as 250 2.0.0 OK
which is a reply back from the SMTP server letting us know that it has successfully sent our mail.
Update (July 6, 2022) - I was setting up a new server with a new email host and ran across an issue while trying to get email sending:
warning: SASL authentication failure: No worthy mechs found
SASL authentication failed; cannot authenticate to server ... : no mechanism available
You may need to install the package libsasl2-modules
and reload postfix using systemctl reload postfix
to be able to authenticate.