The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.
Postfix is a mail transfer agent (MTA), an application used to send and receive email. It can be configured so that it can be used to send emails by local application only. This is useful in situations when you need to regularly send email notifications from your apps or have a lot of outbound traffic that a third-party email service provider won’t allow. It’s also a lighter alternative to running a full-blown SMTP server, while retaining the required functionality.
In this tutorial, you’ll install and configure Postfix as a send-only SMTP server. You’ll also request free TLS certificates from Let’s Encrypt for your domain and encrypt the outbound emails using them.
Note: As of June 22, 2022, DigitalOcean is blocking SMTP for all new accounts. As a part of this new policy, we have partnered with SendGrid so our customers can still send emails with ease. You can learn more about this partnership and get started using SendGrid by checking out our DigitalOcean’s SendGrid Marketplace App.
Setting up and maintaining your own mail server is complicated and time-consuming. For most users, it’s more practical to instead rely on a paid mail service. If you’re considering running your own mail server, we encourage you to review this article on why you may not want to do so.
If you’re sure you want to follow this guide to install and configure Postfix, then you must first have the following:
your_domain
throughout. You can purchase a domain name on Namecheap, get one for free on Freenom, or use the domain registrar of your choice.your_domain
pointing to your server’s public IP address. You can follow this introduction to DigitalOcean DNS for details on how to add them.Note: Your server’s hostname and your Droplet’s name must match your_domain
, because DigitalOcean automatically sets PTR records for the Droplet’s IP address according to its name.
You can verify the server’s hostname by typing hostname
at the command prompt. The output should match the name you gave the Droplet when it was being created.
In this step, you’ll install Postfix. The fastest way is to install the mailutils
package, which bundles Postfix with a few supplementary programs that you’ll use to test sending email.
First, update the package database:
- sudo apt update
Then, install Postfix by running the following command:
- sudo apt install mailutils
Near the end of the installation process, you will be presented with the Postfix configuration window:
The default option is Internet Site
. That’s the recommended option for your use case, so press TAB
, and then ENTER
. If you only see the description text, press TAB
to select OK
, then ENTER
.
If it does not show up automatically, run the following command to start it:
- sudo dpkg-reconfigure postfix
After that, you’ll get another configuration prompt regarding the System mail name:
The System mail name must be the same as the name you assigned to your server when you were creating it. When you’ve finished, press TAB
, followed by ENTER
.
You have now installed Postfix and are ready to start configuring it.
In this step, you’ll configure Postfix to send and receive emails only from the server on which it is running—that is, from localhost
.
For that to happen, you need to configure Postfix to listen only on the loopback interface, the virtual network interface that the server uses to communicate internally. To make the changes, you’ll need to edit the main Postfix configuration file called main.cf
, stored under etc/postfix
.
Open it for editing using your favorite text editor:
- sudo nano /etc/postfix/main.cf
Find the following lines:
. . .
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
. . .
Set the value of the inet_interfaces
setting to loopback-only
:
. . .
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = loopback-only
. . .
Another directive you’ll need to modify is mydestination
, which specifies the list of domains that are delivered via the local_transport
mail delivery transport. By default, the values are similar to these:
. . .
mydestination = $myhostname, your_domain, localhost.com, , localhost
. . .
Change the line to look like this:
. . .
mydestination = localhost.$mydomain, localhost, $myhostname
. . .
If your domain is actually a subdomain and you want the email messages to look as if they were sent from the main domain, you can add the following line to the end of main.cf
:
...
masquerade_domains = your_main_domain
The optional masquerade_domains
setting specifies the domains for which the subdomain will be stripped off in the email address.
When you are done, save and close the file.
Note: If you’re hosting multiple domains on a single server, the other domains can also be passed to Postfix using the mydestination
directive.
Then, restart Postfix by running the following command:
- sudo systemctl restart postfix
You’ve configured Postfix to only send emails from your server. You’ll now test it by sending an example message to an email address.
In this step, you’ll test whether Postfix can send emails to an external email account using the mail
command, which is part of the mailutils
package that you installed in the first step.
To send a test email, run the following command:
- echo "This is the body of the email" | mail -s "This is the subject line" your_email_address
You can change the body and the subject of the email to your liking. Remember to replace your_email_address
with a valid email address that you can access.
Now, check the email address to which you sent this message. You should see the message in your inbox. If it’s not there, check your spam folder. At this point, all emails you send are unencrypted, which makes service providers think it’s likely spam. You’ll set up encryption later, in step 5.
If you receive an error from the mail
command, or you haven’t received a message after a prolonged period of time, check that the Postfix configuration you edited is valid and that your server’s name and hostname are set to your domain.
Note that with this configuration, the address in the From
field for the test emails you send will be in the form of your_user_name@your_domain
, where your_user_name
is the username of the server user you ran the command as.
You have now sent an email from your server and verified that it’s successfully received. In the next step, you’ll set up email forwarding for root
.
In this step, you’ll set up email forwarding for user root
, so that system-generated messages sent to it on your server get forwarded to an external email address.
The /etc/aliases
file contains a list of alternate names for email recipients. Open it for editing:
- sudo nano /etc/aliases
In its default state, it looks like this:
# See man 5 aliases for format
postmaster: root
The only directive present specifies that system-generated emails are sent to root
.
Add the following line to the end of the file:
...
root: your_email_address
With this line, you specify that emails sent to root
end up being forwarded to an email address. Remember to replace your_email_address
with your personal email address. When you are done, save and close the file.
For the change to take effect, run the following command:
- sudo newaliases
Running newaliases
will build up a database of aliases that the mail
command uses, which are taken from the config file you just edited.
Test that sending emails to root
works by running:
- echo "This is the body of the email" | mail -s "This is the subject line" root
You should receive the email at your email address. If it’s not there, check your spam folder.
In this step, you set up forwarding system-generated messages to your email address. You’ll now enable message encryption, so that all emails your server sends are immune to tampering in transit and will be viewed as more legitimate.
You’ll now enable SMTP encryption by requesting a free TLS certificate from Let’s Encrypt for your domain (using Certbot) and configuring Postfix to use it when sending messages.
Ubuntu includes Certbot in their default package repositories, so you can install it by running the following command:
- sudo apt install certbot
When asked for confirmation, type Y
and press ENTER
.
As part of the initial server setup in the prerequisites, you installed ufw
, the uncomplicated firewall. You’ll need to configure it to allow the HTTP port 80
, so that domain verification can be completed. Run the following command to enable it:
- sudo ufw allow 80
The output will look like this:
OutputRule added
Rule added (v6)
Now that the port is open, run Certbot to get a certificate:
- sudo certbot certonly --standalone --rsa-key-size 4096 --agree-tos --preferred-challenges http -d your_domain
This command orders Certbot to issue certificates with an RSA key size of 4096 bits, to run a temporary standalone web server (--standalone
) for verification, and to check via port 80
(--preferred-challenges http
). Remember to replace your_domain
with your domain before running the command, and enter your email address when prompted.
The output will be similar to this:
OutputSaving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for `your_domain`
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/your_domain/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/your_domain/privkey.pem
Your cert will expire on 2020-07-11. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
As written in the notes, your certificate and private key file were saved under /etc/letsencrypt/live/your_domain
.
Now that you have your certificate, open main.cf
for editing:
- sudo nano /etc/postfix/main.cf
Find the following section:
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
Modify it to look like this, replacing your_domain
with your domain where necessary. This will update your TLS settings for Postfix:
# TLS parameters
smtpd_tls_cert_file=/etc/letsencrypt/live/your_domain/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/your_domain/privkey.pem
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
Once you’re done, save and close the file.
Apply the changes by restarting Postfix:
- sudo systemctl restart postfix
Now, try sending an email again:
- echo "This is the body of an encrypted email" | mail -s "This is the subject line" your_email_address
Then, check the email address you provided. It’s possible that you’ll see the message in your inbox immediately, because email providers are much more likely to mark unencrypted messages as spam.
You can check the technical info about the email message in your client to see that the message is indeed encrypted.
You now have a send-only email server, powered by Postfix. Encrypting all outgoing messages is an effective first step to email providers not marking your messages as spam outright. If you are doing this in a development scenario, then this measure should be enough.
However, if your use case is to send emails to potential site users (such as confirmation emails for a message board sign-up), look into setting up SPF records, so that your server’s emails are even more likely to be seen as legitimate.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Hello there, what about the Connection timeout error? Trying to send emails to another domains, like gmail.com? Running this command:
echo "This is the body of the email" | mail -s "This is the subject line" account@gmail.com -aFrom:account@domain.com.br
The log show this all the time:
Apr 8 03:23:44 sapiens postfix/smtp[3391600]: connect to gmail-smtp-in.l.google.com[74.125.197.26]:25: Connection timed out
A telnet from my server doesn`t reach the servel, either:
How to send e-mails from my PHP app?
I know this isn’t about Digital Ocean hosting, but I am working in a test/dev environment before moving to hosting.
I have a locally hosted Ubutnu Server 20.04 LTS available only to the intranet, for running BookStack. I have Postfix instlled and configured properly and sending both aliased and local email to myself for things like errors and unattended upgrades. That’s really all I need, so do I need to install an SSL via Certbot to secure the send only emaail functionality to a single address: mine?
I did use this tutorial to install Certbot to obtain an ssl, but the hostname of my machine does not include my domain name since this is locally hosted and not facing the web. However, I get an error that certbot could not validate the domain when I do enter the legitimate domain of the company I work for.
So… once again, do I need to encrypt the send only emails to my address for unattended updates and local mail to root, or can I leave it at the base setup? And if I do need to encrypt that traffic, I did verify port 80 is available through ufw, but do I also need 443 to validate the domain? Or am I doing this all wrong?
Thanks
There’s a flaw in this tutorial that needs to be corrected. The command for generating the certificate in Step 5 will throw an error saying certbot couldn’t bind to Port 80 as Apache is already using it. You can stop Apache to complete the cert generation process, but it won’t renew automatically. I am not sure what the solution is, but I have temporarily generated it manually (also won’t renew automatically) by using the dns challenge with the following command:
and adding the relevant DNS record.
Hi, thanks for this fantastic and clear guide.
I have however hit an issue.
Step 3 works fine and I get email.
Step 4 - does not work.
I added root alias
and ran
However when I try to send test email
mail.log reports it is trying to send to
to=<root@kinkoo.co.uk>
It seems to have appended the fqdn to the user, and it isn’t using the aliases file.
Any ideas where I went wrong ?
Many thanks
This tutorial has a major gap. In the postfix configuration step you state:
After that, you’ll get another configuration prompt regarding the System mail name: [...] The System mail name must be the same as the name you assigned to your server when you were creating it
.That assumes the droplet was created with a 2ndLevel.tld domain (what are the odds of that? re-ask that question considering a droplet serving multiple domains?). The postfix prompt box actually states that the value entered MUST BE a FullyQualifiedDomainName. Leaving the server assigned name is going to create messes of undelivered mails and endless futzing around - possibly fruitlessly - with configurations.
a) this tutorial, to be helpful, needs to use an example of droplet named
some-fuddy-duddy-name
at is creation as well as a FQDN example (example.org
)b) yes, the test sends the email with a
message-id=<20210826072844.8EDBA13B539@mydomain.ws
andfrom:<myuser@mydomain.ws>
and succeeds. However, sending from an application running on the droplet (rails) via SMTP returns upon execution aDelivered mail 6127475b1551c_bd522b0ad691b9a87987c@some-fuddy-duddy-name.mail (4028.0ms)
Message-ID: <6127475b1551c_bd522b0ad691b9a87987c@some-fuddy-duddy-name.mail>
it does not appear in/var/log/mail.log
and is certainly not delivered.Aside from the potential issues on the application side, it is clear that there is something underneath that needs to be addressed as the message-id is based on something that was set during droplet spin-up (as the application has no knowledge of the droplet’s name). I am definitely struggling in resolving this.
During Step 1, running
sudo apt install mailutils
did not automatically install Postfix, I had to runsudo apt install postfix
separately.I succeeded in sending and receiving a test mail in Step 3.
echo "This is the body of the email" | mail -s "This is the subject line" stevepiercy@my-domain.com
However after setting up aliases in Step 4:
root: stevepiercy@my-domain.com
Reloading aliases:
sudo newaliases
And testing with:
echo "This is the body of the email" | mail -s "This is the subject line" root
I never receive the email.
How can I troubleshoot this issue?
Excellent tutorial! Thanks!!
Hey there,
I’m having a bit of trouble trying to get my emails to be received successfully. They’re being sent, as DMARC tells me that they fail SPF & DKIM checks.
They’re failing because the <auth-results> <spf> <domain> attribute is set to “unknown”, even though I’ve set my droplet’s management name in the D.O. UI and the hostname of the droplet to “mydomain.ca”, as well as following the procedure outlined in step 2.
DMARC tells me that the header-from attribute is correctly set to mydomain.ca, but I’m not sure why the <auth-results> section of DMARC is telling me that SPF & DMARC is failing due to the auth-domain being sent as ‘unknown’.
Here is an excerpt from my DMARC report about the situation:
What settings should I be modifying to get postfix to set the domain attribute properly?
Sorry if this is not the right place to ask this question - I’ll delete it if it goes against community guidelines.
This comment has been deleted