postfix with virtual users (mariadb)
Moderator: frogmaker
Re: postfix with virtual users (mariadb)
About Postfix SMTP Server
Postfix is a state-of-the-art message transport agent (MTA), aka SMTP server, which serves two purposes.
Outgoing emails: It’s responsible for transporting email messages from a mail client/mail user agent (MUA) such as Mozilla Thunderbird and Microsoft Outlook to a remote SMTP server.
Incoming emails: It’s also used to accept emails from other SMTP servers.
Postfix was built by Wietse Venema who is a Unix and security expert. It’s easy to use, designed with security and modularity in mind, with each module running at the lowest possible privilege level required to get the job done. Postfix integrates tightly with Unix/Linux and does not provide functionalities that Unix/Linux already provides. It’s reliable in both simple and stressful conditions.
Postfix was originally designed as a replacement for Sendmail – the old, traditional SMTP server on Unix. In comparison, Postfix is more secure and easier to configure. It is compatible with Sendmail, so if you uninstall Sendmail and replace it with Postfix, your existing scripts and programs will continue to work seamlessly.
In this tutorial, you will learn how to configure Postfix for a single domain name.
Step 1: Choose the Right Hosting Provider
It’s not an easy task to find a VPS (Virtual Private Server) provider suitable for email hosting. Many hosting companies like DigitalOcean blocks port 25. DigitalOcean would not unblock port 25, so you will need to set up SMTP relay to bypass blocking, which can cost you additional money. If you use Vultr VPS, then port 25 is blocked by default. They can unblock it if you open a support ticket, but they may block it again at any time if they decide your email sending activity is not allowed. Vultr actually may re-block it if you use their servers to send newsletters.
Another problem is that big well-known hosting providers like DigitalOcean or Vultr are abused by spammers. Often the server IP address is on several blacklists. Vultr has some entire IP ranges blacklisted.
ScalaHosting is a very good option to run a mail server because
They don’t block port 25.
The IP address isn’t on any email blacklist. (At least this is true in my case. I chose the Dallas data center.) You definitely don’t want to be listed on the dreaded Microsoft Outlook IP blacklist or the SpamRats blacklist. Some blacklists block an entire IP range and you have no way to delist your IP address from this kind of blacklists.
You can edit PTR record to improve email deliverability.
They allow you to send newsletters to your email subscribers with no hourly limits or daily limit, whatsoever. Note that you are not allowed to send spam, also known as unsolicited bulk email. If the recipient doesn’t explicitly give you permission to send emails, and you send emails to them, that’s unsolicited email.
I recommend following the tutorial linked below to properly set up a Linux VPS server on ScalaHosting. Use coupon code linuxbabe2021 on ScalaHosting payment page to save $100 if you choose to pay 12 months upfront.
How to Create a Linux VPS Server on ScalaHosting
You also need a domain name. I registered my domain name from NameCheap because the price is low and they give you whois privacy protection free for life.
Step 2: Configure Mail Server Hostname
I assume you have a sudo user on your Debian server. Run the following commands on the server as root to install sudo.
apt install sudo
Then create a new user and add it to the sudo group.
adduser username
adduser username sudo
Next, switch to the new user.
su - username
By default, Postfix uses your server’s hostname to identify itself when communicating with other MTAs. The hostname can have two forms:
A single word
FQDN (Fully Qualified Domain Name)
The single word form is used mostly on personal computers. Your Linux laptop might be named linux, debian, ubuntu etc.
FQDN (Fully Qualified Domain Name) is commonly used on Internet-facing servers and we should use FQDN on our mail servers. It consists of two parts: a node name and a domain name. For example, mail.linuxbabe.com is an FQDN. mail is the node name, linuxbabe.com is the domain name. FQDN will appear in the smtpd banner. Some MTAs reject messages if your Postfix does not provide FQDN in the smtpd banner. Some MTAs even query DNS to see if FQDN in the smtpd banner resolves to the IP of your mail server.
Log into your server via SSH and enter the following command to see the FQDN form of your server hostname.
hostname -f
If your Debian server doesn’t have an FQDN yet, you can use hostnamectl to set one.
sudo hostnamectl set-hostname your-fqdn
A common FQDN for mail server is mail.example.com. You need to replace example.com with your real domain name. Then log out and log back in to see this change at the command prompt. To log out of your server, run the exit command.
exit
Step 3: Create DNS Records for Your Mail Server
You need to go to your DNS hosting service (usually your domain registrar like NameCheap) to set up DNS records.
In this tutorial, we are creating 3 DNS records for the mail server.
MX record: Mail Exchanger record
A record (and AAAA record): Address record
PTR record: Pointer record
There are also other DNS records for a complete mail server setup. We will discuss them in later parts of this tutorial series.
MX record
An MX record tells other MTAs that your mail server mail.example.com accepts emails for your domain name.
Record Type Name Mail Server Priority
MX @ mail.linuxbabe.com 0
A common name for the MX host is mail.yourdomain.com. You can specify more than one MX record and set priority for your mail servers. A lower number means higher priority in email delivery. Here we only use one MX record and set 0 as the priority value (0 ~ 65535).
Note that when you create the MX record, you should enter @ or your apex domain name (example.com) in the Name field like below. An apex domain name is a domain name without any sub-domain.
email server MX record
A record
An A record maps an FQDN to an IP address. You need to create A record so other SMTP servers can resolve your mail server’s hostame (mail.example.com) to an IP address.
Record Type Name value
A mail <IP-address>
AAAA record
If your server has a public IPv6 address, you also need to add AAAA record for mail.example.com.
Record Type Name value
AAAA mail <IPv6-address>
Hint: If you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating A and AAAA record for mail.example.com. Cloudflare does not support SMTP or IMAP proxy.
PTR record
A pointer record, or PTR record, maps an IP address to an FQDN. It’s the counterpart to the A record and is used for reverse DNS (rDNS) lookup. It tells other MTAs that you really is the owner or manager of this IP address.
Reverse DNS resolution of IP address with PTR record can help with blocking spammers. Many MTAs reject emails if your IP address doesn’t have a PTR record. Even if they don’t reject the email, you still should set a PTR record for your email server so your emails have a better chance of landing in the recipient’s inbox instead of the spam folder.
To check the PTR record for an IP address, you can use the following command. (On Debian server, you can install the dig utility with sudo apt install bind9-utils).
dig -x IP_Address +short
or
host IP_Address
PTR record isn’t managed by your domain registrar. It’s managed by the organization that gives you an IP address. You get IP address from your hosting provider, not from your domain registrar, so you must set PTR record for your IP address in your hosting provider’s control panel. Its value should be your mail server’s hostname: mail.example.com. If your server has a public IPv6 address, then add a PTR record for your IPv6 address as well.
To edit the reverse DNS record for your ScalaHosting VPS, log into ScalaHosting client area, then use the live chat on the bottom-right corner, tell the support team to update the PTR record of your server IP addresss to mail.example.com. Replace example.com with your real domain name.
Note: Gmail will actually check the A record of the hostname specified in the PTR record. If the hostname resolves to the same IP address, Gmail will accept your email. Otherwise, it will reject your email.
Step 4: Install Postfix SMTP Server on Debian Server
On your Debian server, run the following two commands.
sudo apt-get update
sudo apt-get install postfix -y
You will be asked to select a type for mail configuration. Normally, you will want to select the second type: Internet Site.
postfix general type of mail configuration debian
No configuration means the installation process will not configure any parameters in the /etc/postfix/main.cf file.
Internet Site means using Postfix for sending emails to other MTAs and receiving emails from other MTAs.
Internet with smarthost means using postfix to receive email from other MTAs, but using another smart host to relay emails to the recipient.
Satellite system means using a smart host for sending and receiving emails.
Local only means emails are transmitted between local user accounts only.
Next, enter your domain name for the system mail name, i.e. the domain name after @ symbol. For example, my email address is xiao@linuxbabe.com, so I entered linuxbabe.com for the system mail name. This domain name will be appended to addresses that don’t have a domain name specified. Note that if you enter a sub-domain like mail.example.com, you will be able to receive emails destined for @mail.example.com addresses, but not be able to receive emails destined for @example.com addresses.
debian postfix configuraiton system mail name
Once installed, Postfix will be automatically started and a /etc/postfix/main.cf file will be generated. Now we can check Postfix version with this command:
postconf mail_version
On Debian 11 bullseye, the Postfix version is 3.5.6.
mail_version = 3.5.6
The ss (Socket Statistics) utility tells us that the Postfix master process is listening on TCP port 25.
sudo ss -lnpt | grep master
debian-postfix-TCP-port-25
Postfix ships with many binaries under the /usr/sbin/ directory, as can be seen with the following command.
dpkg -L postfix | grep /usr/sbin/
Output:
/usr/sbin/postalias
/usr/sbin/postcat
/usr/sbin/postconf
/usr/sbin/postdrop
/usr/sbin/postfix
/usr/sbin/postfix-add-filter
/usr/sbin/postfix-add-policy
/usr/sbin/postkick
/usr/sbin/postlock
/usr/sbin/postlog
/usr/sbin/postmap
/usr/sbin/postmulti
/usr/sbin/postqueue
/usr/sbin/postsuper
/usr/sbin/posttls-finger
/usr/sbin/qmqp-sink
/usr/sbin/qmqp-source
/usr/sbin/qshape
/usr/sbin/rmail
/usr/sbin/sendmail
/usr/sbin/smtp-sink
/usr/sbin/smtp-source
Step 5: Open TCP Port 25 (inbound) in Firewall
The inbound TCP port 25 needs to be open, so Postfix can receive emails from other SMTP servers. Debian doesn’t enable a firewall by default. Run the following command to install the UFW firewall.
sudo apt install ufw
Then allow SSH traffic with the following command.
sudo ufw allow 22/tcp
If your SSH server is using another port such 6543, then run the following command to allow SSH traffic in the firewall.
sudo ufw allow 6543/tcp
Next, enable the UFW firewall.
sudo ufw enable
Now we can open TCP port 25 (inbound) with the following command.
sudo ufw allow 25/tcp
Then scan open ports on the mail server with an online port scanner. Enter your mail server’s public IP address and select scan all common ports.
debian open TCP port 25 inbound in firewall
You can see from the above screenshot that TCP port 25 is open on my mail server.
Step 6: Checking If TCP Port 25 (outbound) is blocked
The outbound TCP port 25 needs to be open, so Postfix can send emails to other SMTP servers. The outbound TCP port 25 is controlled by your hosting provider, we can install the telnet utility to check if it’s open or blocked.
sudo apt install telnet
Run the following command on your mail server.
telnet gmail-smtp-in.l.google.com 25
If it’s not blocked, you would see messages like below, which indicates an SMTP connection is successfully established to Gmail. (Hint: Type in quit and press Enter to close the connection.)
Trying 74.125.68.26...
Connected to gmail-smtp-in.l.google.com.
Escape character is '^]'.
220 mx.google.com ESMTP y22si1641751pll.208 - gsmtp
If port 25 (outbound) is blocked, you would see something like below, which indicates an SMTP connection can not be established.
Trying 2607:f8b0:400e:c06::1a...
Trying 74.125.195.27...
telnet: Unable to connect to remote host: Connection timed out
In this case, your Postfix can’t send emails to other SMTP servers. Ask your ISP/hosting provider to open the outbound port 25 for you. If they refuse your request, you need to set up SMTP relay to bypass port 25 blocking or use a VPS like ScalaHosting that doesn’t block port 25.
Some folks might be asking, “Can I change port 25 to another port to bypass blocking”? The answer is no. Changing the port works only when you control both the server-side and client-side. When Postfix sends emails, it acts as the SMTP client. The recipient’s mail server acts as the SMTP server. You don’t have control over the receiving SMTP server. SMTP servers are listening on port 25 to receive emails. They expect SMTP clients to hit port 25. There’s no other port for receiving emails. If your Postfix don’t connect to port 25 of the receiving SMTP server, you won’t be able to send emails.
Step 7: Sending Test Email
As a matter of fact, we can now send and receive email from the command line. If your Debian server has a user account called user1, then the email address for this user is user1@example.com. You can send an email to root user root@example.com. You can also send emails to Gmail, Yahoo Mail, or any other email service.
When installing Postfix, a sendmail binary is placed at /usr/sbin/sendmail, which is compatible with the traditional Sendmail SMTP server. You can use Postfix’s sendmail binary to send a test email to your Gmail account like this:
echo "test email" | sendmail your-account@gmail.com
In this simple command, sendmail reads a message from standard input and make “test email” as the message body, then send this message to your Gmail account. You should be able to receive this test email in your Gmail inbox (or spam folder). You can see that although we didn’t specify the from address, Postfix automatically append a domain name for the from address. That’s because we added our domain name in system mail name when installing Postfix.
Note: The From: domain name is determined by the myorigin parameter (aka system mail name) in Postfix, not by the myhostname parameter.
You can try to reply to this test email to see if Postfix can receive email messages. It’s likely that emails sent from your domain are labeled as spam. Don’t worry about it now. We will solve this problem in later parts of this tutorial series.
The inbox for each user is located at /var/spool/mail/<username> or /var/mail/<username> file. If you are unsure where to look for the inbox, use this command.
postconf mail_spool_directory
The Postfix mail log is stored at /var/log/mail.log.
Still Can’t Send Email?
If port 25 (outbound) is not blocked, but you still can’t send emails from your own mail server to your other email address like Gmail, then you can check the mail log (/var/log/mail.log) with the following command.
sudo tail -n 20 /var/log/mail.log
This tells tails to output the last 20 lines of the mail.log file. You can change 20 to 40 if you like.
For example, some folks might see the following lines from the mail.log file.
host gmail-smtp-in.l.google.com[2404:6800:4003:c03::1b] said: 550-5.7.1 [2a0d:7c40
b8b::2] Our system has detected that 550-5.7.1 this message does not meet IPv6 sending guidelines regarding PTR 550-5.7.1 records and authentication. Please review 550-5.7.1 https://support.google.com/mail/?p=IPv6AuthError for more information
This means your mail server is using IPv6 to send the email, but you didn’t set up IPv6 records. You should go to your DNS manager, set AAAA record for mail.example.com, then you should also set PTR record for your IPv6 address. (PTR record is managed by the organization that gives you an IP address.)
You can also open the mail log (/var/log/mail.log) with a command-line text editor, such as VIM, which can be installed by
sudo apt install vim
Then open the mail.log in VIM.
sudo vim /var/log/mail.log
To go to the bottom of this file, press the Caps Lock, then press G. To exit the file, press the Caps Lock to switch back to lower letter mode, then enter :q and press Enter.
Step 8: Using the mail program to Send and Read Email
Now let’s install a command-line MUA (mail user agent).
sudo apt-get install mailutils
To send email, type
mail -a FROM:your-account@example.com username@gmail.com
user@mail:~$ mail -a FROM:xiao@linuxbabe.com username@gmail.com
Cc:
Subject: 2nd test email
I'm sending this email using the mail program.
Enter the subject line and the body text. To tell mail that you have finished writing, press Ctrl+D and mail will send this email message for you.
To read incoming emails, just type mail.
mail
Here’s how to use the mail program to manage your mailbox.
To read the first email message, type 1. If only parts of the message is displayed, press Enter to show the remaining part of the message.
To display message headers starting from message 1, type h.
To show the last screenful of messages, type h$ or z.
To read the next email message, type n.
To delete message 1, type d 1.
To delete message 1, 2 and 3, type d 1 2 3.
To delete messages from 1 to 10, type d 1-10.
To replay to message 1, type reply 1.
To exit out of mail, type q.
Messages that have been opened will be moved from /var/mail/<username> to /home/<username>/mbox file. That means other mail clients can’t read those messages. To prevent this from happening, type x instead of q to exit out of the mail.
Step 8: How To Increase Attachment Size Limit
By default, the attachment cannot be larger than 10MB, which is indicated by the message_size_limit parameter.
postconf | grep message_size_limit
Output:
message_size_limit = 10240000
This parameter defines the size limit for emails originating from your own mail server and for emails coming to your mail server.
To allow attachment of 50MB in size, run the following command.
sudo postconf -e message_size_limit=52428800
When postconf command is invoked with the -e (edit) option, it will try to find the parameter (message_size_limit) in the Postfix main configuration file (/etc/postfix/main.cf) and change the value. If the parameter can’t be found, then it adds the parameter at the end of the file.
Note that the message_size_limit should not be larger than the mailbox_size_limit, otherwise Postfix might not be able to receive emails. The default value of mailbox_size_limit is 51200000 bytes (about 48MB) in the upstream Postfix package. On Debian, the default value is set to 0, as can be seen with
postconf | grep mailbox_size_limit
Output:
mailbox_size_limit = 0
This means that the mailbox has no size limit, which is great.
Restart Postfix for the changes to take effect.
sudo systemctl restart postfix
When sending an email with large attachments from your mail server, you should also beware of the receiving server’s attachment size limit. For example, You can not send an attachment larger than 25MB to a Gmail address.
Step 9: Setting the Postfix Hostname
By default, Postfix SMTP server uses the OS’s hostname. However, the OS hostname might change, so it’s a good practice to set the hostname directly in Postfix configuration file. Open the Postfix main configuration file with a command-line text editor, such as Nano.
sudo nano /etc/postfix/main.cf
Find the myhostname parameter and set mail.example.com as the value. It’s not recommended to use the apex domain example.com as myhostname. Technically you can use the apex domain, but it will create problems in later parts of this tutorial series.
myhostname = mail.example.com
Save and close the file. (To save a file in Nano text editor, press Ctrl+O, then press Enter to confirm. To exit, press Ctrl+X.) Restart Postfix for the change to take effect.
sudo systemctl restart postfix
Once you have set myhostname in Postfix, the OS hostname doesn’t matter anymore. You can change the OS hostname to any hostname you like.
Step 10: Creating Email Alias
There are certain required aliases that you should configure when operating your mail server in a production environment. You can add email alias in the /etc/aliases file, which is a special Postfix lookup table file using a Sendmail-compatible format.
sudo nano /etc/aliases
By default, there are only two lines in this file.
# See man 5 aliases for format
postmaster: root
The first line is a comment. The second line is the only definition of an alias in this file. The left-hand side is the alias name. The right-hand side is the final destination of the email message. So emails for postmaster@example.com will be delivered to root@example.com. The postmaster email address is required by RFC 2142.
Normally we don’t use the root email address. Instead, the postmaster can use a normal login name to access emails. So you can add the following line. Replace username with your real username.
root: username
Ubuntu Postfix email alias
This way, emails for postmaster@example.com and root@example.com will be delivered to username@example.com. Now you can save and close the file. Then rebuild the alias database with the newaliases command
sudo newaliases
Using IPv4 Only
By default, Postfix uses both IPv4 and IPv6 protocols, as can been seen with:
postconf inet_protocols
Output:
inet_protocols = all
If your mail server doesn’t have a public IPv6 address, it’s better to disable IPv6 in Postfix to prevent unnecessary IPv6 connections. Simply run the following command to disable IPv6 in Postfix.
sudo postconf -e "inet_protocols = ipv4"
Then restart Postfix.
sudo systemctl restart postfix
If you don’t know whether your server has public IPv6 address, simply run the following command.
ping 2607:f8b0:4005:808::2005
This command tries to ping Gmail’s IPv6 address. If the ping is successful, then your server can use IPv6. If the ping is unsuccessful, then your server can’t use IPv6. Press Ctrl+C to stop ping.
Upgrading Postfix
If you run sudo apt update command then sudo apt upgrade in the future, and the system is going to upgrade Postfix, you might be prompted to choose a configuration type for Postfix again. This time you should choose No configuration to leave your current configuration file untouched.
postfix no configuration
Next Step
Congrats! Now you have a basic Postfix email server up and running. You can send plain text emails and read incoming emails using the command line. In the next part of this tutorial series, we will learn how to install Dovecot IMAP server and enable TLS encryption, which will allow us to use a desktop mail client like Mozilla Thunderbird to send and receive emails.
Postfix is a state-of-the-art message transport agent (MTA), aka SMTP server, which serves two purposes.
Outgoing emails: It’s responsible for transporting email messages from a mail client/mail user agent (MUA) such as Mozilla Thunderbird and Microsoft Outlook to a remote SMTP server.
Incoming emails: It’s also used to accept emails from other SMTP servers.
Postfix was built by Wietse Venema who is a Unix and security expert. It’s easy to use, designed with security and modularity in mind, with each module running at the lowest possible privilege level required to get the job done. Postfix integrates tightly with Unix/Linux and does not provide functionalities that Unix/Linux already provides. It’s reliable in both simple and stressful conditions.
Postfix was originally designed as a replacement for Sendmail – the old, traditional SMTP server on Unix. In comparison, Postfix is more secure and easier to configure. It is compatible with Sendmail, so if you uninstall Sendmail and replace it with Postfix, your existing scripts and programs will continue to work seamlessly.
In this tutorial, you will learn how to configure Postfix for a single domain name.
Step 1: Choose the Right Hosting Provider
It’s not an easy task to find a VPS (Virtual Private Server) provider suitable for email hosting. Many hosting companies like DigitalOcean blocks port 25. DigitalOcean would not unblock port 25, so you will need to set up SMTP relay to bypass blocking, which can cost you additional money. If you use Vultr VPS, then port 25 is blocked by default. They can unblock it if you open a support ticket, but they may block it again at any time if they decide your email sending activity is not allowed. Vultr actually may re-block it if you use their servers to send newsletters.
Another problem is that big well-known hosting providers like DigitalOcean or Vultr are abused by spammers. Often the server IP address is on several blacklists. Vultr has some entire IP ranges blacklisted.
ScalaHosting is a very good option to run a mail server because
They don’t block port 25.
The IP address isn’t on any email blacklist. (At least this is true in my case. I chose the Dallas data center.) You definitely don’t want to be listed on the dreaded Microsoft Outlook IP blacklist or the SpamRats blacklist. Some blacklists block an entire IP range and you have no way to delist your IP address from this kind of blacklists.
You can edit PTR record to improve email deliverability.
They allow you to send newsletters to your email subscribers with no hourly limits or daily limit, whatsoever. Note that you are not allowed to send spam, also known as unsolicited bulk email. If the recipient doesn’t explicitly give you permission to send emails, and you send emails to them, that’s unsolicited email.
I recommend following the tutorial linked below to properly set up a Linux VPS server on ScalaHosting. Use coupon code linuxbabe2021 on ScalaHosting payment page to save $100 if you choose to pay 12 months upfront.
How to Create a Linux VPS Server on ScalaHosting
You also need a domain name. I registered my domain name from NameCheap because the price is low and they give you whois privacy protection free for life.
Step 2: Configure Mail Server Hostname
I assume you have a sudo user on your Debian server. Run the following commands on the server as root to install sudo.
apt install sudo
Then create a new user and add it to the sudo group.
adduser username
adduser username sudo
Next, switch to the new user.
su - username
By default, Postfix uses your server’s hostname to identify itself when communicating with other MTAs. The hostname can have two forms:
A single word
FQDN (Fully Qualified Domain Name)
The single word form is used mostly on personal computers. Your Linux laptop might be named linux, debian, ubuntu etc.
FQDN (Fully Qualified Domain Name) is commonly used on Internet-facing servers and we should use FQDN on our mail servers. It consists of two parts: a node name and a domain name. For example, mail.linuxbabe.com is an FQDN. mail is the node name, linuxbabe.com is the domain name. FQDN will appear in the smtpd banner. Some MTAs reject messages if your Postfix does not provide FQDN in the smtpd banner. Some MTAs even query DNS to see if FQDN in the smtpd banner resolves to the IP of your mail server.
Log into your server via SSH and enter the following command to see the FQDN form of your server hostname.
hostname -f
If your Debian server doesn’t have an FQDN yet, you can use hostnamectl to set one.
sudo hostnamectl set-hostname your-fqdn
A common FQDN for mail server is mail.example.com. You need to replace example.com with your real domain name. Then log out and log back in to see this change at the command prompt. To log out of your server, run the exit command.
exit
Step 3: Create DNS Records for Your Mail Server
You need to go to your DNS hosting service (usually your domain registrar like NameCheap) to set up DNS records.
In this tutorial, we are creating 3 DNS records for the mail server.
MX record: Mail Exchanger record
A record (and AAAA record): Address record
PTR record: Pointer record
There are also other DNS records for a complete mail server setup. We will discuss them in later parts of this tutorial series.
MX record
An MX record tells other MTAs that your mail server mail.example.com accepts emails for your domain name.
Record Type Name Mail Server Priority
MX @ mail.linuxbabe.com 0
A common name for the MX host is mail.yourdomain.com. You can specify more than one MX record and set priority for your mail servers. A lower number means higher priority in email delivery. Here we only use one MX record and set 0 as the priority value (0 ~ 65535).
Note that when you create the MX record, you should enter @ or your apex domain name (example.com) in the Name field like below. An apex domain name is a domain name without any sub-domain.
email server MX record
A record
An A record maps an FQDN to an IP address. You need to create A record so other SMTP servers can resolve your mail server’s hostame (mail.example.com) to an IP address.
Record Type Name value
A mail <IP-address>
AAAA record
If your server has a public IPv6 address, you also need to add AAAA record for mail.example.com.
Record Type Name value
AAAA mail <IPv6-address>
Hint: If you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating A and AAAA record for mail.example.com. Cloudflare does not support SMTP or IMAP proxy.
PTR record
A pointer record, or PTR record, maps an IP address to an FQDN. It’s the counterpart to the A record and is used for reverse DNS (rDNS) lookup. It tells other MTAs that you really is the owner or manager of this IP address.
Reverse DNS resolution of IP address with PTR record can help with blocking spammers. Many MTAs reject emails if your IP address doesn’t have a PTR record. Even if they don’t reject the email, you still should set a PTR record for your email server so your emails have a better chance of landing in the recipient’s inbox instead of the spam folder.
To check the PTR record for an IP address, you can use the following command. (On Debian server, you can install the dig utility with sudo apt install bind9-utils).
dig -x IP_Address +short
or
host IP_Address
PTR record isn’t managed by your domain registrar. It’s managed by the organization that gives you an IP address. You get IP address from your hosting provider, not from your domain registrar, so you must set PTR record for your IP address in your hosting provider’s control panel. Its value should be your mail server’s hostname: mail.example.com. If your server has a public IPv6 address, then add a PTR record for your IPv6 address as well.
To edit the reverse DNS record for your ScalaHosting VPS, log into ScalaHosting client area, then use the live chat on the bottom-right corner, tell the support team to update the PTR record of your server IP addresss to mail.example.com. Replace example.com with your real domain name.
Note: Gmail will actually check the A record of the hostname specified in the PTR record. If the hostname resolves to the same IP address, Gmail will accept your email. Otherwise, it will reject your email.
Step 4: Install Postfix SMTP Server on Debian Server
On your Debian server, run the following two commands.
sudo apt-get update
sudo apt-get install postfix -y
You will be asked to select a type for mail configuration. Normally, you will want to select the second type: Internet Site.
postfix general type of mail configuration debian
No configuration means the installation process will not configure any parameters in the /etc/postfix/main.cf file.
Internet Site means using Postfix for sending emails to other MTAs and receiving emails from other MTAs.
Internet with smarthost means using postfix to receive email from other MTAs, but using another smart host to relay emails to the recipient.
Satellite system means using a smart host for sending and receiving emails.
Local only means emails are transmitted between local user accounts only.
Next, enter your domain name for the system mail name, i.e. the domain name after @ symbol. For example, my email address is xiao@linuxbabe.com, so I entered linuxbabe.com for the system mail name. This domain name will be appended to addresses that don’t have a domain name specified. Note that if you enter a sub-domain like mail.example.com, you will be able to receive emails destined for @mail.example.com addresses, but not be able to receive emails destined for @example.com addresses.
debian postfix configuraiton system mail name
Once installed, Postfix will be automatically started and a /etc/postfix/main.cf file will be generated. Now we can check Postfix version with this command:
postconf mail_version
On Debian 11 bullseye, the Postfix version is 3.5.6.
mail_version = 3.5.6
The ss (Socket Statistics) utility tells us that the Postfix master process is listening on TCP port 25.
sudo ss -lnpt | grep master
debian-postfix-TCP-port-25
Postfix ships with many binaries under the /usr/sbin/ directory, as can be seen with the following command.
dpkg -L postfix | grep /usr/sbin/
Output:
/usr/sbin/postalias
/usr/sbin/postcat
/usr/sbin/postconf
/usr/sbin/postdrop
/usr/sbin/postfix
/usr/sbin/postfix-add-filter
/usr/sbin/postfix-add-policy
/usr/sbin/postkick
/usr/sbin/postlock
/usr/sbin/postlog
/usr/sbin/postmap
/usr/sbin/postmulti
/usr/sbin/postqueue
/usr/sbin/postsuper
/usr/sbin/posttls-finger
/usr/sbin/qmqp-sink
/usr/sbin/qmqp-source
/usr/sbin/qshape
/usr/sbin/rmail
/usr/sbin/sendmail
/usr/sbin/smtp-sink
/usr/sbin/smtp-source
Step 5: Open TCP Port 25 (inbound) in Firewall
The inbound TCP port 25 needs to be open, so Postfix can receive emails from other SMTP servers. Debian doesn’t enable a firewall by default. Run the following command to install the UFW firewall.
sudo apt install ufw
Then allow SSH traffic with the following command.
sudo ufw allow 22/tcp
If your SSH server is using another port such 6543, then run the following command to allow SSH traffic in the firewall.
sudo ufw allow 6543/tcp
Next, enable the UFW firewall.
sudo ufw enable
Now we can open TCP port 25 (inbound) with the following command.
sudo ufw allow 25/tcp
Then scan open ports on the mail server with an online port scanner. Enter your mail server’s public IP address and select scan all common ports.
debian open TCP port 25 inbound in firewall
You can see from the above screenshot that TCP port 25 is open on my mail server.
Step 6: Checking If TCP Port 25 (outbound) is blocked
The outbound TCP port 25 needs to be open, so Postfix can send emails to other SMTP servers. The outbound TCP port 25 is controlled by your hosting provider, we can install the telnet utility to check if it’s open or blocked.
sudo apt install telnet
Run the following command on your mail server.
telnet gmail-smtp-in.l.google.com 25
If it’s not blocked, you would see messages like below, which indicates an SMTP connection is successfully established to Gmail. (Hint: Type in quit and press Enter to close the connection.)
Trying 74.125.68.26...
Connected to gmail-smtp-in.l.google.com.
Escape character is '^]'.
220 mx.google.com ESMTP y22si1641751pll.208 - gsmtp
If port 25 (outbound) is blocked, you would see something like below, which indicates an SMTP connection can not be established.
Trying 2607:f8b0:400e:c06::1a...
Trying 74.125.195.27...
telnet: Unable to connect to remote host: Connection timed out
In this case, your Postfix can’t send emails to other SMTP servers. Ask your ISP/hosting provider to open the outbound port 25 for you. If they refuse your request, you need to set up SMTP relay to bypass port 25 blocking or use a VPS like ScalaHosting that doesn’t block port 25.
Some folks might be asking, “Can I change port 25 to another port to bypass blocking”? The answer is no. Changing the port works only when you control both the server-side and client-side. When Postfix sends emails, it acts as the SMTP client. The recipient’s mail server acts as the SMTP server. You don’t have control over the receiving SMTP server. SMTP servers are listening on port 25 to receive emails. They expect SMTP clients to hit port 25. There’s no other port for receiving emails. If your Postfix don’t connect to port 25 of the receiving SMTP server, you won’t be able to send emails.
Step 7: Sending Test Email
As a matter of fact, we can now send and receive email from the command line. If your Debian server has a user account called user1, then the email address for this user is user1@example.com. You can send an email to root user root@example.com. You can also send emails to Gmail, Yahoo Mail, or any other email service.
When installing Postfix, a sendmail binary is placed at /usr/sbin/sendmail, which is compatible with the traditional Sendmail SMTP server. You can use Postfix’s sendmail binary to send a test email to your Gmail account like this:
echo "test email" | sendmail your-account@gmail.com
In this simple command, sendmail reads a message from standard input and make “test email” as the message body, then send this message to your Gmail account. You should be able to receive this test email in your Gmail inbox (or spam folder). You can see that although we didn’t specify the from address, Postfix automatically append a domain name for the from address. That’s because we added our domain name in system mail name when installing Postfix.
Note: The From: domain name is determined by the myorigin parameter (aka system mail name) in Postfix, not by the myhostname parameter.
You can try to reply to this test email to see if Postfix can receive email messages. It’s likely that emails sent from your domain are labeled as spam. Don’t worry about it now. We will solve this problem in later parts of this tutorial series.
The inbox for each user is located at /var/spool/mail/<username> or /var/mail/<username> file. If you are unsure where to look for the inbox, use this command.
postconf mail_spool_directory
The Postfix mail log is stored at /var/log/mail.log.
Still Can’t Send Email?
If port 25 (outbound) is not blocked, but you still can’t send emails from your own mail server to your other email address like Gmail, then you can check the mail log (/var/log/mail.log) with the following command.
sudo tail -n 20 /var/log/mail.log
This tells tails to output the last 20 lines of the mail.log file. You can change 20 to 40 if you like.
For example, some folks might see the following lines from the mail.log file.
host gmail-smtp-in.l.google.com[2404:6800:4003:c03::1b] said: 550-5.7.1 [2a0d:7c40
This means your mail server is using IPv6 to send the email, but you didn’t set up IPv6 records. You should go to your DNS manager, set AAAA record for mail.example.com, then you should also set PTR record for your IPv6 address. (PTR record is managed by the organization that gives you an IP address.)
You can also open the mail log (/var/log/mail.log) with a command-line text editor, such as VIM, which can be installed by
sudo apt install vim
Then open the mail.log in VIM.
sudo vim /var/log/mail.log
To go to the bottom of this file, press the Caps Lock, then press G. To exit the file, press the Caps Lock to switch back to lower letter mode, then enter :q and press Enter.
Step 8: Using the mail program to Send and Read Email
Now let’s install a command-line MUA (mail user agent).
sudo apt-get install mailutils
To send email, type
mail -a FROM:your-account@example.com username@gmail.com
user@mail:~$ mail -a FROM:xiao@linuxbabe.com username@gmail.com
Cc:
Subject: 2nd test email
I'm sending this email using the mail program.
Enter the subject line and the body text. To tell mail that you have finished writing, press Ctrl+D and mail will send this email message for you.
To read incoming emails, just type mail.
Here’s how to use the mail program to manage your mailbox.
To read the first email message, type 1. If only parts of the message is displayed, press Enter to show the remaining part of the message.
To display message headers starting from message 1, type h.
To show the last screenful of messages, type h$ or z.
To read the next email message, type n.
To delete message 1, type d 1.
To delete message 1, 2 and 3, type d 1 2 3.
To delete messages from 1 to 10, type d 1-10.
To replay to message 1, type reply 1.
To exit out of mail, type q.
Messages that have been opened will be moved from /var/mail/<username> to /home/<username>/mbox file. That means other mail clients can’t read those messages. To prevent this from happening, type x instead of q to exit out of the mail.
Step 8: How To Increase Attachment Size Limit
By default, the attachment cannot be larger than 10MB, which is indicated by the message_size_limit parameter.
postconf | grep message_size_limit
Output:
message_size_limit = 10240000
This parameter defines the size limit for emails originating from your own mail server and for emails coming to your mail server.
To allow attachment of 50MB in size, run the following command.
sudo postconf -e message_size_limit=52428800
When postconf command is invoked with the -e (edit) option, it will try to find the parameter (message_size_limit) in the Postfix main configuration file (/etc/postfix/main.cf) and change the value. If the parameter can’t be found, then it adds the parameter at the end of the file.
Note that the message_size_limit should not be larger than the mailbox_size_limit, otherwise Postfix might not be able to receive emails. The default value of mailbox_size_limit is 51200000 bytes (about 48MB) in the upstream Postfix package. On Debian, the default value is set to 0, as can be seen with
postconf | grep mailbox_size_limit
Output:
mailbox_size_limit = 0
This means that the mailbox has no size limit, which is great.
Restart Postfix for the changes to take effect.
sudo systemctl restart postfix
When sending an email with large attachments from your mail server, you should also beware of the receiving server’s attachment size limit. For example, You can not send an attachment larger than 25MB to a Gmail address.
Step 9: Setting the Postfix Hostname
By default, Postfix SMTP server uses the OS’s hostname. However, the OS hostname might change, so it’s a good practice to set the hostname directly in Postfix configuration file. Open the Postfix main configuration file with a command-line text editor, such as Nano.
sudo nano /etc/postfix/main.cf
Find the myhostname parameter and set mail.example.com as the value. It’s not recommended to use the apex domain example.com as myhostname. Technically you can use the apex domain, but it will create problems in later parts of this tutorial series.
myhostname = mail.example.com
Save and close the file. (To save a file in Nano text editor, press Ctrl+O, then press Enter to confirm. To exit, press Ctrl+X.) Restart Postfix for the change to take effect.
sudo systemctl restart postfix
Once you have set myhostname in Postfix, the OS hostname doesn’t matter anymore. You can change the OS hostname to any hostname you like.
Step 10: Creating Email Alias
There are certain required aliases that you should configure when operating your mail server in a production environment. You can add email alias in the /etc/aliases file, which is a special Postfix lookup table file using a Sendmail-compatible format.
sudo nano /etc/aliases
By default, there are only two lines in this file.
# See man 5 aliases for format
postmaster: root
The first line is a comment. The second line is the only definition of an alias in this file. The left-hand side is the alias name. The right-hand side is the final destination of the email message. So emails for postmaster@example.com will be delivered to root@example.com. The postmaster email address is required by RFC 2142.
Normally we don’t use the root email address. Instead, the postmaster can use a normal login name to access emails. So you can add the following line. Replace username with your real username.
root: username
Ubuntu Postfix email alias
This way, emails for postmaster@example.com and root@example.com will be delivered to username@example.com. Now you can save and close the file. Then rebuild the alias database with the newaliases command
sudo newaliases
Using IPv4 Only
By default, Postfix uses both IPv4 and IPv6 protocols, as can been seen with:
postconf inet_protocols
Output:
inet_protocols = all
If your mail server doesn’t have a public IPv6 address, it’s better to disable IPv6 in Postfix to prevent unnecessary IPv6 connections. Simply run the following command to disable IPv6 in Postfix.
sudo postconf -e "inet_protocols = ipv4"
Then restart Postfix.
sudo systemctl restart postfix
If you don’t know whether your server has public IPv6 address, simply run the following command.
ping 2607:f8b0:4005:808::2005
This command tries to ping Gmail’s IPv6 address. If the ping is successful, then your server can use IPv6. If the ping is unsuccessful, then your server can’t use IPv6. Press Ctrl+C to stop ping.
Upgrading Postfix
If you run sudo apt update command then sudo apt upgrade in the future, and the system is going to upgrade Postfix, you might be prompted to choose a configuration type for Postfix again. This time you should choose No configuration to leave your current configuration file untouched.
postfix no configuration
Next Step
Congrats! Now you have a basic Postfix email server up and running. You can send plain text emails and read incoming emails using the command line. In the next part of this tutorial series, we will learn how to install Dovecot IMAP server and enable TLS encryption, which will allow us to use a desktop mail client like Mozilla Thunderbird to send and receive emails.
Re: postfix with virtual users (mariadb)
This is part 2 of building your own secure email server on Debian from scratch tutorial series. In part 1, we showed you how to set up a basic Postfix SMTP server. In this tutorial, we are going to configure the email server so that we can receive and send emails using a desktop email client like Mozilla Thunderbird or Microsoft Outlook.
To be able to send emails using a desktop email client, we need to enable the submission service in Postfix.
To receive emails using a desktop email client, we can install an open-source IMAP server named Dovecot on the Debian server.
And to encrypt our communications, we can install a free TLS certificate issued by Let’s Encrypt.
Step 1: Open Ports in Firewall
Debian doesn’t enable firewall by default. If you have enabled the UFW firewall, then you need to run the following command to open email related ports in firewall.
sudo ufw allow 80,443,587,465,143,993/tcp
If you use POP3 to fetch emails (I personally don’t), then also open port 110 and 995.
sudo ufw allow 110,995/tcp
Step 2: Securing Email Server Traffic with TLS Certificate
When we configure our desktop email clients, It’s always a good idea to enable TLS encryption to prevent hackers from snooping on our emails. We can easily obtain a free TLS certificate from Let’s Encrypt. Issue the following commands to install Let’s Encrypt client (certbot) on Debian server from the default software repository.
sudo apt update
sudo apt dist-upgrade
sudo apt install certbot
If you don’t have a web server running yet, I recommend you install one (Apache or Nginx), because it’s easier to obtain and install TLS certificate with a web server than using other methods. And in a later tutorial, I will show you how to set up webmail, which requires running a web server.
If you choose to use Apache web server, you need to install the Apache plugin. (The following command will install Apache web server if it’s not already installed on your system.)
sudo apt install python3-certbot-apache
If you choose use Nginx web server, then install the Nginx plugin. (The following command will install Nginx web server if it’s not already installed on your system.)
sudo apt install python3-certbot-nginx
Obtaining TLS Certificate with Apache Web Server
We create an Apache virtual host for mail.example.com before obtaining Let’s Encrypt TLS certificate. Create the virtual host file:
sudo nano /etc/apache2/sites-available/mail.example.com.conf
Then paste the following text into the file.
<VirtualHost *:80>
ServerName mail.example.com
DocumentRoot /var/www/html/
</VirtualHost>
Save and close the file. Enable this virtual host.
sudo a2ensite mail.example.com.conf
Then disable the default virtual host, because it might interfere with other virtual hosts.
sudo a2dissite 000-default
Reload Apache for the changes to take effect.
sudo systemctl reload apache2
Once the virtual host is created and enabled, run the following command to obtain Let’s Encrypt TLS certificate.
sudo certbot certonly -a apache --agree-tos --no-eff-email --staple-ocsp --email you@example.com -d mail.example.com
Where:
certonly: obtain the TLS certificate but don’t install it in the web server.
-a apache: Use the Apache plugin for authentication
--agree-tos: Agree to terms of service.
--no-eff-email: Don’t receive emails from EFF foundation.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS connection.
--email: Enter your email address, which is used for important notifications and account recovery.
-d: domain, aka your mail server hostname.
Substitute the red text with your actual data. You should see the following which means the certificate is successfully obtained. You can also see the directory under which your cert is stored.
postfix-tls-letsencrypt-certbot
Obtaining TLS Certificate with Nginx Web Server
We create an Nginx virtual host for mail.example.com before obtaining Let’s Encrypt TLS certificate. Create the virtual host file:
sudo nano /etc/nginx/conf.d/mail.example.com.conf
Next, paste the following text into the file.
server {
listen 80;
listen [::]:80;
server_name mail.example.com;
root /var/www/html/;
location ~ /.well-known/acme-challenge {
allow all;
}
}
Save and close the file. Reload Nginx for the changes to take effect.
sudo systemctl reload nginx
Once the virtual host is created and enabled, run the following command to obtain Let’s Encrypt certificate with Nginx plugin.
sudo certbot certonly -a nginx --agree-tos --no-eff-email --staple-ocsp --email you@example.com -d mail.example.com
Where:
certonly: obtain the TLS certificate but don’t install it in the web server.
-a nginx: Use the Nginx plugin for authentication
--agree-tos: Agree to terms of service.
--no-eff-email: Don’t receive emails from EFF foundation.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS connection.
--email: Enter your email address, which is used for important notifications and account recovery.
-d: domain, aka your mail server hostname.
You should see the following which means the certificate is successfully obtained. You can also see the directory under which your cert is stored.
dovecot-tls-letsencrypt-certbot
Step 3: Enable Submission Service in Postfix
To send emails from a desktop email client, we need to enable the submission service of Postfix so that the email client can submit emails to Postfix SMTP server. Edit the master.cf file.
sudo nano /etc/postfix/master.cf
In submission section, uncomment or add the following lines. Please allow at least one whitespace (tab or spacebar) before each -o. In postfix configurations, a preceding whitespace character means that this line is continuation of the previous line. (By default the submission section is commented out. You can copy the following lines and paste them into the file, so you don’t have to manually uncomment or add new text.)
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
The above configuration enables the submission daemon of Postfix and requires TLS encryption. So later on our desktop email client can connect to the submission daemon in TLS encryption. The submission daemon listens on TCP port 587. STARTTLS is used to encrypt communications between email client and the submission daemon.
Microsoft Outlook mail client only supports submission over port 465. If you are going to use Microsoft Outlook, then you also need to enable submission service on port 465 by adding the following lines in the file.
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
Enable Submission Service in Postfix
Save and close the file.
Hint: The SMTP protocol is used when an email client submits emails to an SMTP server.
Next, we need to specify the location of TLS certificate and private key in Postfix configuration file. Edit main.cf file.
sudo nano /etc/postfix/main.cf
Edit the TLS parameter as follows. Remember to replace mail.example.com with your real hostname.
#Enable TLS Encryption when Postfix receives incoming emails
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level=may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#Enable TLS Encryption when Postfix sends outgoing emails
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
#Enforce TLSv1.3 or TLSv1.2
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
Your Let’s Encrypt certificate and private key are stored under /etc/letsencrypt/live/mail.example.com/ directory.
postfix tls parameters
Save and close the file. Then restart Postfix.
sudo systemctl restart postfix
If you run the following command, you will see Postfix is now listening on port 587 and 465.
sudo ss -lnpt | grep master
postfix master submission port 587 smtps port 465
Step 4: Installing Dovecot IMAP Server
Enter the following command to install Dovecot core package and the IMAP daemon package on Debian server.
sudo apt install dovecot-core dovecot-imapd
If you use POP3 to fetch emails, then also install the dovecot-pop3d package.
sudo apt install dovecot-pop3d
Check Dovecot version:
dovecot --version
Sample output:
2.3.13 (89f716dc2)
Step 5: Enabling IMAP/POP3 Protocol
Edit the main config file.
sudo nano /etc/dovecot/dovecot.conf
Add the following line to enable IMAP protocol.
protocols = imap
debian-dovecot-enable-IMAP-protocol
If you use POP3 to fetch emails, then also add POP3 protocol.
protocols = imap pop3
Save and close the file.
Step 6: Configuring Mailbox Location
By default, Postfix and Dovecot use mbox format to store emails. Each user’s emails are stored in a single file /var/mail/username. You can run the following command to find the mail spool directory.
postconf mail_spool_directory
Sample output:
mail_spool_directory = /var/mail
However, nowadays it’s almost always you want to use the Maildir format to store email messages. The config file for mailbox location is /etc/dovecot/conf.d/10-mail.conf.
sudo nano /etc/dovecot/conf.d/10-mail.conf
The default configuration uses mbox mail format.
mail_location = mbox:~/mail:INBOX=/var/mail/%u
Change it to the following to make Dovecot use the Maildir format. Email messages will be stored under the Maildir directory under each user’s home directory.
mail_location = maildir:~/Maildir
Save and close the file. Then add dovecot to the mail group so that Dovecot can read the INBOX.
sudo adduser dovecot mail
Step 7: Using Dovecot to Deliver Email to Message Store
Although we configured Dovecot to store emails in Maildir format, by default, Postfix uses its built-in local delivery agent (LDA) to move inbound emails to the message store (inbox, sent, trash, Junk, etc), and it will be saved in mbox format.
We need to configure Postfix to pass incoming emails to Dovecot, via the LMTP protocol, which is a simplified version of SMTP, so incoming emails will saved in Maildir format by Dovecot. LMTP allows for a highly scalable and reliable mail system. It also allows us to use the sieve plugin to filter inbound messages to different folders.
Install the Dovecot LMTP Server.
sudo apt install dovecot-lmtpd
Edit the Dovecot main configuration file.
sudo nano /etc/dovecot/dovecot.conf
Add lmtp to the supported protocols.
protocols = imap lmtp
Save and close the file. Then edit the Dovecot 10-master.conf file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Change the lmtp service definition to the following. Be careful about the syntax. Each opening bracket needs to be paired with a closing bracket.
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
dovecot-lmtp-debian
Save and close the file.
Next, edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of the file. The first line tells Postfix to deliver incoming emails to local message store via the Dovecot LMTP server. The second line disables SMTPUTF8 in Postfix, because Dovecot-LMTP doesn’t support this email extension.
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
Save and close the file.
Step 8: Configuring User Authentication Mechanism
Edit the authentication config file.
sudo nano /etc/dovecot/conf.d/10-auth.conf
Uncomment the following line.
disable_plaintext_auth = yes
It will disable plaintext authentication when there’s no SSL/TLS encryption. Then find the following line,
#auth_username_format = %Lu
Uncomment it and change its value to %n.
auth_username_format = %n
By default, when Dovecot tries to find or deliver emails for a user, it uses the full email address. Since in this part, we only set up canonical mailbox users (using OS users as mailbox users), Dovecot can’t find the mailbox user in full domain format (username@example.com), so we need to set auth_username_format = %n to drop the domain part, then Dovecot should be able to find the mailbox user. This also allows us to use the full email address (username@example.com) to log in.
debian-dovecot-auth_username_format
Next, find the following line.
auth_mechanisms = plain
This line only enables the PLAIN authentication mechanism. LOGIN is another authentication mechanism you probably want to add to support older email clients.
auth_mechanisms = plain login
Save and close the file.
Step 9: Configuring SSL/TLS Encryption
Edit SSL/TLS config file.
sudo nano /etc/dovecot/conf.d/10-ssl.conf
Change ssl = yes to ssl = required to enforce encryption.
ssl = required
Then find the following lines.
ssl_cert = </etc/dovecot/private/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.key
By default, Dovecot uses a self-signed TLS certificate. Replace them with the following values, which specify the location of your Let’s Encrypt TLS certificate and private key. Don’t leave out the < character. It’s necessary.
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
Find the following line.
#ssl_prefer_server_ciphers = no
It’s a good practice to prefer the server’s order of ciphers over client’s. So uncomment this line and change the value to yes.
ssl_prefer_server_ciphers = yes
The find the following line.
#ssl_min_protocol = TLSv1
Change it to the following to disable insecure SSLv3, TLSv1, and TLSv1.1 protocols.
ssl_min_protocol = TLSv1.2
Save and close the file.
Step 10: Configuring SASL Authentication
Edit the following file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Change service auth section to the following so that Postfix can find the Dovecot authentication server. Please be careful about the syntax. Every opening bracket should be terminated by a closing bracket.
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
postfix-smtp-auth-debian
Save and close the file.
After you save and close all the above config files, restart Postfix and Dovecot.
sudo systemctl restart postfix dovecot
Dovecot will be listening on port 143 (IMAP) and 993 (IMAPS), as can be seen with:
sudo ss -lnpt | grep dovecot
debian-dovecot-imap-server-port-143-993
If there’s a configuration error, dovecot will fail to restart, so it’s a good idea to check if Dovecot is running with the following command.
sudo systemctl status dovecot
Step 11: Configure Desktop Email Client
Now open up your desktop email client such as Mozilla Thunderbird. Go to Edit -> Account Settings -> Account Actions -> Add Mail Account to add a mail account.
In the incoming server section, select IMAP protocol, enter mail.your-domain.com as the server name, choose port 143 and STARTTLS. Choose normal password as the authentication method.
In the outgoing section, select SMTP protocol, enter mail.your-domain.com as the server name, choose port 587 and STARTTLS. Choose normal password as the authentication method.
debian-postfix-dovecot-letsencrypt
Hint: You can also use port 993 with SSL/TLS encryption for IMAP, and use port 465 with SSL/TLS encryption for SMTP. You should NOT use port 25 as the SMTP port in mail clients to submit outgoing emails.
You should now be able to connect to your own email server and also send and receive emails with your desktop email client!
We use local Unix accounts as email addresses, as we did in part 1. For example, if you have a user named user1 on your Debian server, then you have an email address: user1@example.com, and the password for the email address is the same password for the user1 user. To create a local Unix account, run
sudo adduser user1
Note: Dovecot doesn’t allow you to log in with the root account. You need to create separate user accounts.
You can list all available mailbox users with:
sudo doveadm user '*'
It’s recommended to restart Dovecot after adding users, so Dovecot can recognize new mailbox users.
sudo systemctl restart dovecot
Troubleshooting Tips
As a rule of thumb, you should always check the mail log (/var/log/mail.log) on your mail server when an error happens. The following is a list of specific errors and troubleshooting tips.
Can’t login from Mail Clients
If you can’t log into your mail server from a desktop mail client, scan your mail server to find if the ports (TCP 587, 465, 143, and 993) are open. Note that you should run the following command from another Linux computer or server. If you run it on your mail server, then the ports will always appear to be open.
sudo nmap mail.example.com
And check if Dovecot is running.
systemctl status dovecot
You can also check the mail log (/var/log/mail.log), which may give you some clues. If Dovecot fails to start, the error might not be logged to the /var/log/mail.log file, you can run the following command to see what’s wrong.
sudo journalctl -eu dovecot
For example, some folks may have the following error in the journal.
doveconf: Fatal: Error in configuration file /etc/dovecot/conf.d/10-master.conf line 78: Unknown setting
Most of the time, it’s a simple syntax error, like a missing curly bracket. Open the configuration file, go to the specified line and fix the error.
If you find the following error message in the mail log
imap-login: Error: Failed to initialize SSL server context: Can't load DH parameters: error:1408518A:SSL routines:ssl3_ctx_ctrl:dh key too small
Then open the Dovecot TLS configuration file.
sudo nano /etc/dovecot/conf.d/10-ssl.conf
Add the following line in this file.
ssl_dh = </etc/dovecot/dh.pem
Save and close the file. Then generate the DH parameter file with:
sudo openssl dhparam -out /etc/dovecot/dh.pem 4096
Restart Dovecot for the changes to take effect.
Cloudflare DNS
As I said in part 1, if you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating DNS A record and AAAA record for the hostname of your mail server. Cloudflare doesn’t support SMTP or IMAP proxy.
Relay Access Denied
If you see the “relay access denied” error when trying to send emails from a mail client, it’s most likely that you use port 25 as the SMTP port in your mail client. As I said a while ago, you should use port 587 or 465 as the SMTP port in mail clients (Mozilla Thunberbird, Microsoft Outlook, etc) to submit outgoing emails. Port 25 should be used for SMTP server to SMTP server communications.
postfix dovecot relay access denied
If you see the following “relay access denied” error in the /var/log/mail.log file when trying to send emails from other mail services like Gmail to your own mail server, it’s likely that yourdomain.com is not in the list of $mydestination parameter.
NOQUEUE: reject: RCPT from mail-il1-f180.google.com[209.85.166.180]: 454 4.7.1 <xiao@inuxbabe.com>: Relay access denied; from=<someone@gmail.com> to=<xiao@linuxbabe.com> proto=ESMTP helo=<mail-il1-f180.google.com>
You can display the current value of $mydestination with:
postconf mydestination
Some folks might not have the main domain name in the list like so:
mydestination = $myhostname, localhost.$mydomain, localhost
Then run the following command to add the main domain name to the list.
sudo postconf -e "mydestination = example.com, \$myhostname, localhost.\$mydomain, localhost"
Reload Postfix for the changes to take effect.
sudo systemctl reload postfix
User Doesn’t Exist
If you see the following error message in the mail log (/var/log/mail.log), it’s likely that you forgot to set auth_username_format = %n In /etc/dovecot/conf.d/10-auth.conf file.
mail postfix/lmtp[2256]: 68E00FC1A5: to=, relay=mail.example.com[private/dovecot-lmtp], delay=509, delays=509/0.03/0.03/0.02, dsn=5.1.1, status=bounced (host mail.example.com[private/dovecot-lmtp] said: 550 5.1.1 User doesn't exist: user1@example.com (in reply to RCPT TO command))
iOS Mail App
If you use the iOS Mail app to log into your mail server and encounter the following error.
ios the mail server is not responding
You can try to fix it by enforcing SSL encryption, for both SMTP and IMAP.
ios mail enforce SSL encryption
Fun fact: It seems the iOS Mail app has difficulty in supporting STARTTLS on IMAP port 143, but it supports STARTTLS on the submission port 587.
If you encounter the “No password provided” error in the iOS Mail app, it’s likely that you have a typo when entering the username in the Mail account settings, or you didn’t enable SSL in the Mail account settings.
ios mail no password provided
Unable to Receive Email From Gmail, Hotmail, Yahoo Mail, etc
If you can’t receive emails from Gmail, Hotmail, Yahoo Mail, etc, here are the possible causes:
Your MX record is wrong, or not propagated to the Internet yet.
Your mail server hostname doesn’t have DNS A record, or not propagated to the Internet yet.
Your firewall doesn’t allow incoming connection to port 25. Maybe your mail server is behind a NAT?
Postfix isn’t listening on the public IP address.
Check the mail log (/var/log/mail.log) to find out if there are other errors in your Postfix and Dovecot configuration.
You can use the Network Tools Email Checker to test if your SMTP server is reachable from the Internet. Just enter your domain email address and click the Go button. As you can see from the screenshot below, it successfully found my domain’s MX record and my SMTP server is reachable from the Internet.
email checker
If your SMTP servers isn’t reachable from the Internet, then you have a problem in the first 4 items. If your SMTP server is reachable from the Internet, but you still can’t receive emails, check the mail log (/var/log/mail.log) to find out if there is any errors in your Postfix and Dovecot configuration.
Step 12: Auto-Renew TLS Certificate
You can create Cron job to automatically renew TLS certificate. Simply open root user’s crontab file.
sudo crontab -e
If you use Apache web server, add the following line at the bottom of the file.
@daily certbot renew --quiet && systemctl reload postfix dovecot apache2
If you are using Nginx web server, then add the following line.
@daily certbot renew --quiet && systemctl reload postfix dovecot nginx
Reloading Postfix, Dovecot and the web server is necessary to make these programs pick up the new certificate and private key.
Step 13: Dovecot Automatic Restart
If for any reason your Dovecot process is killed, you need to run the following command to restart it.
sudo systemctl restart dovecot
Instead of manually typing this command, we can make Dovecot automatically restart by editing the dovecot.service systemd service unit. To override the default systemd service configuration, we create a separate directory.
sudo mkdir -p /etc/systemd/system/dovecot.service.d/
Then create a file under this directory.
sudo nano /etc/systemd/system/dovecot.service.d/restart.conf
Add the following lines in the file, which will make Dovecot automatically restart 5 seconds after a failure is detected.
[Service]
Restart=always
RestartSec=5s
Save and close the file. Then reload systemd for the changes to take effect.
sudo systemctl daemon-reload
To check if this would work, kill Dovecot with:
sudo pkill dovecot
Then check Dovecot status. You will find Dovecot automatically restarted.
systemctl status dovecot
Wrapping Up
I hope this article helped you set up Postfix and Dovecot on Debian server. In part 3, I will show you how to create virtual mailboxes.
As always, if you found this post useful, subscribe to our newsletter to get more tips and tricks. Take care
To be able to send emails using a desktop email client, we need to enable the submission service in Postfix.
To receive emails using a desktop email client, we can install an open-source IMAP server named Dovecot on the Debian server.
And to encrypt our communications, we can install a free TLS certificate issued by Let’s Encrypt.
Step 1: Open Ports in Firewall
Debian doesn’t enable firewall by default. If you have enabled the UFW firewall, then you need to run the following command to open email related ports in firewall.
sudo ufw allow 80,443,587,465,143,993/tcp
If you use POP3 to fetch emails (I personally don’t), then also open port 110 and 995.
sudo ufw allow 110,995/tcp
Step 2: Securing Email Server Traffic with TLS Certificate
When we configure our desktop email clients, It’s always a good idea to enable TLS encryption to prevent hackers from snooping on our emails. We can easily obtain a free TLS certificate from Let’s Encrypt. Issue the following commands to install Let’s Encrypt client (certbot) on Debian server from the default software repository.
sudo apt update
sudo apt dist-upgrade
sudo apt install certbot
If you don’t have a web server running yet, I recommend you install one (Apache or Nginx), because it’s easier to obtain and install TLS certificate with a web server than using other methods. And in a later tutorial, I will show you how to set up webmail, which requires running a web server.
If you choose to use Apache web server, you need to install the Apache plugin. (The following command will install Apache web server if it’s not already installed on your system.)
sudo apt install python3-certbot-apache
If you choose use Nginx web server, then install the Nginx plugin. (The following command will install Nginx web server if it’s not already installed on your system.)
sudo apt install python3-certbot-nginx
Obtaining TLS Certificate with Apache Web Server
We create an Apache virtual host for mail.example.com before obtaining Let’s Encrypt TLS certificate. Create the virtual host file:
sudo nano /etc/apache2/sites-available/mail.example.com.conf
Then paste the following text into the file.
<VirtualHost *:80>
ServerName mail.example.com
DocumentRoot /var/www/html/
</VirtualHost>
Save and close the file. Enable this virtual host.
sudo a2ensite mail.example.com.conf
Then disable the default virtual host, because it might interfere with other virtual hosts.
sudo a2dissite 000-default
Reload Apache for the changes to take effect.
sudo systemctl reload apache2
Once the virtual host is created and enabled, run the following command to obtain Let’s Encrypt TLS certificate.
sudo certbot certonly -a apache --agree-tos --no-eff-email --staple-ocsp --email you@example.com -d mail.example.com
Where:
certonly: obtain the TLS certificate but don’t install it in the web server.
-a apache: Use the Apache plugin for authentication
--agree-tos: Agree to terms of service.
--no-eff-email: Don’t receive emails from EFF foundation.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS connection.
--email: Enter your email address, which is used for important notifications and account recovery.
-d: domain, aka your mail server hostname.
Substitute the red text with your actual data. You should see the following which means the certificate is successfully obtained. You can also see the directory under which your cert is stored.
postfix-tls-letsencrypt-certbot
Obtaining TLS Certificate with Nginx Web Server
We create an Nginx virtual host for mail.example.com before obtaining Let’s Encrypt TLS certificate. Create the virtual host file:
sudo nano /etc/nginx/conf.d/mail.example.com.conf
Next, paste the following text into the file.
server {
listen 80;
listen [::]:80;
server_name mail.example.com;
root /var/www/html/;
location ~ /.well-known/acme-challenge {
allow all;
}
}
Save and close the file. Reload Nginx for the changes to take effect.
sudo systemctl reload nginx
Once the virtual host is created and enabled, run the following command to obtain Let’s Encrypt certificate with Nginx plugin.
sudo certbot certonly -a nginx --agree-tos --no-eff-email --staple-ocsp --email you@example.com -d mail.example.com
Where:
certonly: obtain the TLS certificate but don’t install it in the web server.
-a nginx: Use the Nginx plugin for authentication
--agree-tos: Agree to terms of service.
--no-eff-email: Don’t receive emails from EFF foundation.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS connection.
--email: Enter your email address, which is used for important notifications and account recovery.
-d: domain, aka your mail server hostname.
You should see the following which means the certificate is successfully obtained. You can also see the directory under which your cert is stored.
dovecot-tls-letsencrypt-certbot
Step 3: Enable Submission Service in Postfix
To send emails from a desktop email client, we need to enable the submission service of Postfix so that the email client can submit emails to Postfix SMTP server. Edit the master.cf file.
sudo nano /etc/postfix/master.cf
In submission section, uncomment or add the following lines. Please allow at least one whitespace (tab or spacebar) before each -o. In postfix configurations, a preceding whitespace character means that this line is continuation of the previous line. (By default the submission section is commented out. You can copy the following lines and paste them into the file, so you don’t have to manually uncomment or add new text.)
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
The above configuration enables the submission daemon of Postfix and requires TLS encryption. So later on our desktop email client can connect to the submission daemon in TLS encryption. The submission daemon listens on TCP port 587. STARTTLS is used to encrypt communications between email client and the submission daemon.
Microsoft Outlook mail client only supports submission over port 465. If you are going to use Microsoft Outlook, then you also need to enable submission service on port 465 by adding the following lines in the file.
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
Enable Submission Service in Postfix
Save and close the file.
Hint: The SMTP protocol is used when an email client submits emails to an SMTP server.
Next, we need to specify the location of TLS certificate and private key in Postfix configuration file. Edit main.cf file.
sudo nano /etc/postfix/main.cf
Edit the TLS parameter as follows. Remember to replace mail.example.com with your real hostname.
#Enable TLS Encryption when Postfix receives incoming emails
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level=may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#Enable TLS Encryption when Postfix sends outgoing emails
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
#Enforce TLSv1.3 or TLSv1.2
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
Your Let’s Encrypt certificate and private key are stored under /etc/letsencrypt/live/mail.example.com/ directory.
postfix tls parameters
Save and close the file. Then restart Postfix.
sudo systemctl restart postfix
If you run the following command, you will see Postfix is now listening on port 587 and 465.
sudo ss -lnpt | grep master
postfix master submission port 587 smtps port 465
Step 4: Installing Dovecot IMAP Server
Enter the following command to install Dovecot core package and the IMAP daemon package on Debian server.
sudo apt install dovecot-core dovecot-imapd
If you use POP3 to fetch emails, then also install the dovecot-pop3d package.
sudo apt install dovecot-pop3d
Check Dovecot version:
dovecot --version
Sample output:
2.3.13 (89f716dc2)
Step 5: Enabling IMAP/POP3 Protocol
Edit the main config file.
sudo nano /etc/dovecot/dovecot.conf
Add the following line to enable IMAP protocol.
protocols = imap
debian-dovecot-enable-IMAP-protocol
If you use POP3 to fetch emails, then also add POP3 protocol.
protocols = imap pop3
Save and close the file.
Step 6: Configuring Mailbox Location
By default, Postfix and Dovecot use mbox format to store emails. Each user’s emails are stored in a single file /var/mail/username. You can run the following command to find the mail spool directory.
postconf mail_spool_directory
Sample output:
mail_spool_directory = /var/mail
However, nowadays it’s almost always you want to use the Maildir format to store email messages. The config file for mailbox location is /etc/dovecot/conf.d/10-mail.conf.
sudo nano /etc/dovecot/conf.d/10-mail.conf
The default configuration uses mbox mail format.
mail_location = mbox:~/mail:INBOX=/var/mail/%u
Change it to the following to make Dovecot use the Maildir format. Email messages will be stored under the Maildir directory under each user’s home directory.
mail_location = maildir:~/Maildir
Save and close the file. Then add dovecot to the mail group so that Dovecot can read the INBOX.
sudo adduser dovecot mail
Step 7: Using Dovecot to Deliver Email to Message Store
Although we configured Dovecot to store emails in Maildir format, by default, Postfix uses its built-in local delivery agent (LDA) to move inbound emails to the message store (inbox, sent, trash, Junk, etc), and it will be saved in mbox format.
We need to configure Postfix to pass incoming emails to Dovecot, via the LMTP protocol, which is a simplified version of SMTP, so incoming emails will saved in Maildir format by Dovecot. LMTP allows for a highly scalable and reliable mail system. It also allows us to use the sieve plugin to filter inbound messages to different folders.
Install the Dovecot LMTP Server.
sudo apt install dovecot-lmtpd
Edit the Dovecot main configuration file.
sudo nano /etc/dovecot/dovecot.conf
Add lmtp to the supported protocols.
protocols = imap lmtp
Save and close the file. Then edit the Dovecot 10-master.conf file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Change the lmtp service definition to the following. Be careful about the syntax. Each opening bracket needs to be paired with a closing bracket.
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
dovecot-lmtp-debian
Save and close the file.
Next, edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of the file. The first line tells Postfix to deliver incoming emails to local message store via the Dovecot LMTP server. The second line disables SMTPUTF8 in Postfix, because Dovecot-LMTP doesn’t support this email extension.
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
Save and close the file.
Step 8: Configuring User Authentication Mechanism
Edit the authentication config file.
sudo nano /etc/dovecot/conf.d/10-auth.conf
Uncomment the following line.
disable_plaintext_auth = yes
It will disable plaintext authentication when there’s no SSL/TLS encryption. Then find the following line,
#auth_username_format = %Lu
Uncomment it and change its value to %n.
auth_username_format = %n
By default, when Dovecot tries to find or deliver emails for a user, it uses the full email address. Since in this part, we only set up canonical mailbox users (using OS users as mailbox users), Dovecot can’t find the mailbox user in full domain format (username@example.com), so we need to set auth_username_format = %n to drop the domain part, then Dovecot should be able to find the mailbox user. This also allows us to use the full email address (username@example.com) to log in.
debian-dovecot-auth_username_format
Next, find the following line.
auth_mechanisms = plain
This line only enables the PLAIN authentication mechanism. LOGIN is another authentication mechanism you probably want to add to support older email clients.
auth_mechanisms = plain login
Save and close the file.
Step 9: Configuring SSL/TLS Encryption
Edit SSL/TLS config file.
sudo nano /etc/dovecot/conf.d/10-ssl.conf
Change ssl = yes to ssl = required to enforce encryption.
ssl = required
Then find the following lines.
ssl_cert = </etc/dovecot/private/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.key
By default, Dovecot uses a self-signed TLS certificate. Replace them with the following values, which specify the location of your Let’s Encrypt TLS certificate and private key. Don’t leave out the < character. It’s necessary.
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
Find the following line.
#ssl_prefer_server_ciphers = no
It’s a good practice to prefer the server’s order of ciphers over client’s. So uncomment this line and change the value to yes.
ssl_prefer_server_ciphers = yes
The find the following line.
#ssl_min_protocol = TLSv1
Change it to the following to disable insecure SSLv3, TLSv1, and TLSv1.1 protocols.
ssl_min_protocol = TLSv1.2
Save and close the file.
Step 10: Configuring SASL Authentication
Edit the following file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Change service auth section to the following so that Postfix can find the Dovecot authentication server. Please be careful about the syntax. Every opening bracket should be terminated by a closing bracket.
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
postfix-smtp-auth-debian
Save and close the file.
After you save and close all the above config files, restart Postfix and Dovecot.
sudo systemctl restart postfix dovecot
Dovecot will be listening on port 143 (IMAP) and 993 (IMAPS), as can be seen with:
sudo ss -lnpt | grep dovecot
debian-dovecot-imap-server-port-143-993
If there’s a configuration error, dovecot will fail to restart, so it’s a good idea to check if Dovecot is running with the following command.
sudo systemctl status dovecot
Step 11: Configure Desktop Email Client
Now open up your desktop email client such as Mozilla Thunderbird. Go to Edit -> Account Settings -> Account Actions -> Add Mail Account to add a mail account.
In the incoming server section, select IMAP protocol, enter mail.your-domain.com as the server name, choose port 143 and STARTTLS. Choose normal password as the authentication method.
In the outgoing section, select SMTP protocol, enter mail.your-domain.com as the server name, choose port 587 and STARTTLS. Choose normal password as the authentication method.
debian-postfix-dovecot-letsencrypt
Hint: You can also use port 993 with SSL/TLS encryption for IMAP, and use port 465 with SSL/TLS encryption for SMTP. You should NOT use port 25 as the SMTP port in mail clients to submit outgoing emails.
You should now be able to connect to your own email server and also send and receive emails with your desktop email client!
We use local Unix accounts as email addresses, as we did in part 1. For example, if you have a user named user1 on your Debian server, then you have an email address: user1@example.com, and the password for the email address is the same password for the user1 user. To create a local Unix account, run
sudo adduser user1
Note: Dovecot doesn’t allow you to log in with the root account. You need to create separate user accounts.
You can list all available mailbox users with:
sudo doveadm user '*'
It’s recommended to restart Dovecot after adding users, so Dovecot can recognize new mailbox users.
sudo systemctl restart dovecot
Troubleshooting Tips
As a rule of thumb, you should always check the mail log (/var/log/mail.log) on your mail server when an error happens. The following is a list of specific errors and troubleshooting tips.
Can’t login from Mail Clients
If you can’t log into your mail server from a desktop mail client, scan your mail server to find if the ports (TCP 587, 465, 143, and 993) are open. Note that you should run the following command from another Linux computer or server. If you run it on your mail server, then the ports will always appear to be open.
sudo nmap mail.example.com
And check if Dovecot is running.
systemctl status dovecot
You can also check the mail log (/var/log/mail.log), which may give you some clues. If Dovecot fails to start, the error might not be logged to the /var/log/mail.log file, you can run the following command to see what’s wrong.
sudo journalctl -eu dovecot
For example, some folks may have the following error in the journal.
doveconf: Fatal: Error in configuration file /etc/dovecot/conf.d/10-master.conf line 78: Unknown setting
Most of the time, it’s a simple syntax error, like a missing curly bracket. Open the configuration file, go to the specified line and fix the error.
If you find the following error message in the mail log
imap-login: Error: Failed to initialize SSL server context: Can't load DH parameters: error:1408518A:SSL routines:ssl3_ctx_ctrl:dh key too small
Then open the Dovecot TLS configuration file.
sudo nano /etc/dovecot/conf.d/10-ssl.conf
Add the following line in this file.
ssl_dh = </etc/dovecot/dh.pem
Save and close the file. Then generate the DH parameter file with:
sudo openssl dhparam -out /etc/dovecot/dh.pem 4096
Restart Dovecot for the changes to take effect.
Cloudflare DNS
As I said in part 1, if you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating DNS A record and AAAA record for the hostname of your mail server. Cloudflare doesn’t support SMTP or IMAP proxy.
Relay Access Denied
If you see the “relay access denied” error when trying to send emails from a mail client, it’s most likely that you use port 25 as the SMTP port in your mail client. As I said a while ago, you should use port 587 or 465 as the SMTP port in mail clients (Mozilla Thunberbird, Microsoft Outlook, etc) to submit outgoing emails. Port 25 should be used for SMTP server to SMTP server communications.
postfix dovecot relay access denied
If you see the following “relay access denied” error in the /var/log/mail.log file when trying to send emails from other mail services like Gmail to your own mail server, it’s likely that yourdomain.com is not in the list of $mydestination parameter.
NOQUEUE: reject: RCPT from mail-il1-f180.google.com[209.85.166.180]: 454 4.7.1 <xiao@inuxbabe.com>: Relay access denied; from=<someone@gmail.com> to=<xiao@linuxbabe.com> proto=ESMTP helo=<mail-il1-f180.google.com>
You can display the current value of $mydestination with:
postconf mydestination
Some folks might not have the main domain name in the list like so:
mydestination = $myhostname, localhost.$mydomain, localhost
Then run the following command to add the main domain name to the list.
sudo postconf -e "mydestination = example.com, \$myhostname, localhost.\$mydomain, localhost"
Reload Postfix for the changes to take effect.
sudo systemctl reload postfix
User Doesn’t Exist
If you see the following error message in the mail log (/var/log/mail.log), it’s likely that you forgot to set auth_username_format = %n In /etc/dovecot/conf.d/10-auth.conf file.
mail postfix/lmtp[2256]: 68E00FC1A5: to=, relay=mail.example.com[private/dovecot-lmtp], delay=509, delays=509/0.03/0.03/0.02, dsn=5.1.1, status=bounced (host mail.example.com[private/dovecot-lmtp] said: 550 5.1.1 User doesn't exist: user1@example.com (in reply to RCPT TO command))
iOS Mail App
If you use the iOS Mail app to log into your mail server and encounter the following error.
ios the mail server is not responding
You can try to fix it by enforcing SSL encryption, for both SMTP and IMAP.
ios mail enforce SSL encryption
Fun fact: It seems the iOS Mail app has difficulty in supporting STARTTLS on IMAP port 143, but it supports STARTTLS on the submission port 587.
If you encounter the “No password provided” error in the iOS Mail app, it’s likely that you have a typo when entering the username in the Mail account settings, or you didn’t enable SSL in the Mail account settings.
ios mail no password provided
Unable to Receive Email From Gmail, Hotmail, Yahoo Mail, etc
If you can’t receive emails from Gmail, Hotmail, Yahoo Mail, etc, here are the possible causes:
Your MX record is wrong, or not propagated to the Internet yet.
Your mail server hostname doesn’t have DNS A record, or not propagated to the Internet yet.
Your firewall doesn’t allow incoming connection to port 25. Maybe your mail server is behind a NAT?
Postfix isn’t listening on the public IP address.
Check the mail log (/var/log/mail.log) to find out if there are other errors in your Postfix and Dovecot configuration.
You can use the Network Tools Email Checker to test if your SMTP server is reachable from the Internet. Just enter your domain email address and click the Go button. As you can see from the screenshot below, it successfully found my domain’s MX record and my SMTP server is reachable from the Internet.
email checker
If your SMTP servers isn’t reachable from the Internet, then you have a problem in the first 4 items. If your SMTP server is reachable from the Internet, but you still can’t receive emails, check the mail log (/var/log/mail.log) to find out if there is any errors in your Postfix and Dovecot configuration.
Step 12: Auto-Renew TLS Certificate
You can create Cron job to automatically renew TLS certificate. Simply open root user’s crontab file.
sudo crontab -e
If you use Apache web server, add the following line at the bottom of the file.
@daily certbot renew --quiet && systemctl reload postfix dovecot apache2
If you are using Nginx web server, then add the following line.
@daily certbot renew --quiet && systemctl reload postfix dovecot nginx
Reloading Postfix, Dovecot and the web server is necessary to make these programs pick up the new certificate and private key.
Step 13: Dovecot Automatic Restart
If for any reason your Dovecot process is killed, you need to run the following command to restart it.
sudo systemctl restart dovecot
Instead of manually typing this command, we can make Dovecot automatically restart by editing the dovecot.service systemd service unit. To override the default systemd service configuration, we create a separate directory.
sudo mkdir -p /etc/systemd/system/dovecot.service.d/
Then create a file under this directory.
sudo nano /etc/systemd/system/dovecot.service.d/restart.conf
Add the following lines in the file, which will make Dovecot automatically restart 5 seconds after a failure is detected.
[Service]
Restart=always
RestartSec=5s
Save and close the file. Then reload systemd for the changes to take effect.
sudo systemctl daemon-reload
To check if this would work, kill Dovecot with:
sudo pkill dovecot
Then check Dovecot status. You will find Dovecot automatically restarted.
systemctl status dovecot
Wrapping Up
I hope this article helped you set up Postfix and Dovecot on Debian server. In part 3, I will show you how to create virtual mailboxes.
As always, if you found this post useful, subscribe to our newsletter to get more tips and tricks. Take care
Re: postfix with virtual users (mariadb)
In previous articles, we discussed how to set up your own mail server on Debian from scratch. In part 1 and part 2 of this tutorial series, we learned how to set up Postfix SMTP server and Dovecot IMAP server, but so far we can only have email addresses for users with local Unix account. This tutorial is going to show you how to create virtual mailboxes on Debian mail server with PostfixAdmin, which is an open-source web-based interface to configure and manage a Postfix-based email server for many domains and users.
With virtual mailboxes, we don’t need to create local Unix account for each email address. If you are going to set up a mail server for a company or organization, it’s always better to have an easy way to create virtual mailboxes in a web-based interface, which also allows users to change their passwords. That’s where PostfixAdmin comes in.
PostfixAdmin Features
manage mailboxes, virtual domains, and aliases
vacation/out-of-office messages (Personally I think this feature is better done in Roundcube Webmail.)
alias domains (forwarding one domain to another with recipient validation)
users can manage their own mailbox (change alias, password, and vacation message)
quota support for single mailboxes and total quota of a domain
fetchmail integration: You can fetch emails from your original email address to your new email address.
command line client postfixadmin-cli for those who don’t want to click around in a web interface
Note: Once you finish part 3, you can no longer use local Unix accounts as email addresses. You must create email addresses from the PostfixAdmin web interface.
Requirements
I assume that you have followed part 1 and part 2 of this tutorial series. If you followed mail server tutorials on other websites, I recommend purging your configurations (sudo apt purge postfix dovecot-core) and start over with my tutorial series, so you are not going to be confused by different setup processes.
Once the above requirements are met, let’s install and configure PostfixAdmin.
Step 1: Install MariaDB Database Server
PostfixAdmin is written in PHP and requires a database (MySQL/MariaDB, PostgreSQL or SQLite). This article will use MariaDB database, which is a drop-in replacement for MySQL. It is developed by former members of MySQL team who are concerned that Oracle might turn MySQL into a closed-source product. Enter the following commands to install MariaDB on Debian.
sudo apt update
sudo apt install mariadb-server mariadb-client
After it’s installed, MariaDB server should be automatically started. Use systemctl to check its status.
systemctl status mariadb
Sample output:
● mariadb.service - MariaDB 10.5.12 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2021-10-29 23:44:59 EDT; 1min 12s ago
Docs: man:mariadbd(8)
https://mariadb.com/kb/en/library/systemd/
Process: 435260 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)
Process: 435261 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
Process: 435263 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] && VAR= || VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ] && systemctl set-environment _WSREP_START_POSITIO>
Process: 435322 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
Process: 435324 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)
Main PID: 435310 (mariadbd)
Status: "Taking your SQL requests now..."
Tasks: 9 (limit: 1095)
Memory: 78.7M
CPU: 741ms
CGroup: /system.slice/mariadb.service
└─435310 /usr/sbin/mariadbd
If it’s not running, start it with this command:
sudo systemctl start mariadb
To enable MariaDB to automatically start at boot time, run
sudo systemctl enable mariadb
Now run the post-installation security script.
sudo mysql_secure_installation
When it asks you to enter MariaDB root password, press Enter key as the root password isn’t set yet. Your root account is already secured, so you should answer n to not set password.
debian bullseye secure mariadb
Next, you can press Enter to answer all remaining questions, which will remove anonymous user, disable remote root login and remove test database. This step is a basic requirement for MariaDB database security. (Notice that Y is capitalized, which means it is the default answer. )
install mariadb debian bullseye
Step 2: Download PostfixAdmin on Debian Server
PostfixAdmin is included in the default Debian repository. However, I don’t recommend it, as it can create problems when you upgrade the Debian system to a new version. So I will show you how to install the latest version of PostfixAdmin. Go to PostfixAdmin Gitbub page to download the latest version. You can use the wget tool to download it from command line. The download link is always available in the format below. If a new version comes out, simply replace 3.3.10 with the new version number.
sudo apt install wget
wget https://github.com/postfixadmin/postfix ... .10.tar.gz
Once downloaded, extract the archive to the /var/www/ directory and rename it to postfixadmin.
sudo mkdir -p /var/www/
sudo tar xvf postfixadmin-3.3.10.tar.gz -C /var/www/
sudo mv /var/www/postfixadmin-postfixadmin-3.3.10 /var/www/postfixadmin
Step 3: Setting Up Permissions
PostfixAdmin requires a templates_c directory, and the web server needs read and write access to this directory, so run the following commands.
sudo mkdir /var/www/postfixadmin/templates_c
sudo apt install acl
sudo setfacl -R -m u:www-data:rwx /var/www/postfixadmin/templates_c/
Starting with Dovecot 2.3.11, the web server user needs permission to read Let’s Encrypt TLS certificate in order to do password hashing. Run the following two commands to grant permissions.
sudo setfacl -R -m u:www-data:rx /etc/letsencrypt/live/ /etc/letsencrypt/archive/
Step 4: Create a Database and User for PostfixAdmin
Log into MySQL/MariaDB shell as root with the following command.
sudo mysql -u root
Once you are logged in, create a database for PostfixAdmin using the following command. I named it postfixadmin, but you can use whatever name you like. (Don’t leave out the semicolon.)
create database postfixadmin;
Then enter the command below to create a database user for PostfixAdmin. This command also grants all privileges of postfixadmin database to the user. Replace postfixadmin_password with your preferred password. Note that the password should not contain the # character, or you might not be able to log in later.
grant all privileges on postfixadmin.* to 'postfixadmin'@'localhost' identified by 'postfixadmin_password';
Flush the privileges table for the changes to take effect and then get out of MariaDB shell.
flush privileges;
exit;
Step 5: Configure PostfixAdmin
The default PostfixAdmin configuration file is config.inc.php. We need to create a config.local.php file and add custom configurations.
sudo nano /var/www/postfixadmin/config.local.php
Add the following lines in the file, so PostfixAdmin can connect to MySQL/MariaDB database. Replace postfixadmin_password with the real PostfixAdmin password created in step 4.
<?php
$CONF['configured'] = true;
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_port'] = '3306';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'postfixadmin_password';
$CONF['database_name'] = 'postfixadmin';
$CONF['encrypt'] = 'dovecot:ARGON2I';
$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5";
if(@file_exists('/usr/bin/doveadm')) { // @ to silence openbase_dir stuff; see https://github.com/postfixadmin/postfixadmin/issues/171
$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5"; # debian
}
Save and close the file. Note that we will use the ARGON2I password scheme.
Step 6: Create Apache Virtual Host or Nginx Config File for PostfixAdmin
Apache
If you use Apache web server, create a virtual host for PostfixAdmin.
sudo nano /etc/apache2/sites-available/postfixadmin.conf
Put the following text into the file. Replace postfixadmin.example.com with your real domain name and don’t forget to set DNS A record for it.
<VirtualHost *:80>
ServerName postfixadmin.example.com
DocumentRoot /var/www/postfixadmin/public
ErrorLog ${APACHE_LOG_DIR}/postfixadmin_error.log
CustomLog ${APACHE_LOG_DIR}/postfixadmin_access.log combined
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/postfixadmin/>
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
</VirtualHost>
Save and close the file. Then enable this virtual host with:
sudo a2ensite postfixadmin.conf
Reload Apache for the changes to take effect.
sudo systemctl reload apache2
Now you should be able to see the PostfixAdmin web-based install wizard at http://postfixadmin.example.com/setup.php.
Nginx
If you use Nginx web server, create a virtual host for PostfixAdmin.
sudo nano /etc/nginx/conf.d/postfixadmin.conf
Put the following text into the file. Replace postfixadmin.example.com with your real domain name and don’t forget to set DNS A record for it.
server {
listen 80;
listen [::]:80;
server_name postfixadmin.example.com;
root /var/www/postfixadmin/public/;
index index.php index.html;
access_log /var/log/nginx/postfixadmin_access.log;
error_log /var/log/nginx/postfixadmin_error.log;
location / {
try_files $uri $uri/ /index.php;
}
location ~ ^/(.+\.php)$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
Note: Debian 11 ships PHP7.4 and Debian 10 ships with PHP7.3. If you use Debian 10, then change php7.4-fpm to php7.3-fpm in the above file.
Save and close the file. Then test Nginx configuration.
sudo nginx -t
If the test is successful, reload Nginx for the changes to take effect.
sudo systemctl reload nginx
Now you should be able to see the PostfixAdmin web-based install wizard at http://postfixadmin.example.com/setup.php.
Step 7: Install Required and Recommended PHP Modules
Run the following command to install PHP modules required or recommended by PostfixAdmin on Debian 11.
sudo apt install php7.4-fpm php7.4-imap php7.4-mbstring php7.4-mysql php7.4-json php7.4-curl php7.4-zip php7.4-xml php7.4-bz2 php7.4-intl php7.4-gmp
If you use Debian 10, run the following command.
sudo apt install php7.3-fpm php7.3-imap php7.3-mbstring php7.3-mysql php7.3-json php7.3-curl php7.3-zip php7.3-xml php7.3-bz2 php7.3-intl php7.3-gmp
Then restart Apache. (If you use Nginx, you don’t need to restart Nginx.)
sudo systemctl restart apache2
Step 8: Enabling HTTPS
To encrypt the HTTP traffic, we can enable HTTPS by installing a free TLS certificate issued from Let’s Encrypt. Run the following command to install Let’s Encrypt client (certbot) on Debian server.
sudo apt install certbot
If you use Apache, install the Certbot Apache plugin.
sudo apt install python3-certbot-apache
And run this command to obtain and install TLS certificate.
sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d postfixadmin.example.com
If you use Nginx, then you also need to install the Certbot Nginx plugin.
sudo apt install python3-certbot-nginx
Next, run the following command to obtain and install TLS certificate.
sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d postfixadmin.example.com
Where
--nginx: Use the nginx plugin.
--apache: Use the Apache plugin.
--agree-tos: Agree to terms of service.
--redirect: Force HTTPS by 301 redirect.
--hsts: Add the Strict-Transport-Security header to every HTTP response. Forcing browser to always use TLS for the domain. Defends against SSL/TLS Stripping.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS.
The certificate should now be obtained and automatically installed, which is indicated by the message below.
postfixadmin ubuntu https
Step 9: Enable Statistics in Dovecot
PostfixAdmin needs to read Dovecot statistics. Edit the Dovecot configuration file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Add the following lines to the end of this file.
service stats {
unix_listener stats-reader {
user = www-data
group = www-data
mode = 0660
}
unix_listener stats-writer {
user = www-data
group = www-data
mode = 0660
}
}
Save and close the file. Then add the web server to the dovecot group.
sudo gpasswd -a www-data dovecot
Restart Dovecot.
sudo systemctl restart dovecot
Step 10: Finish the Installation in Web Browser
Go to postfixadmin.example.com/setup.php to run the web-based setup wizard. First, you need to create a setup password for PostfixAdmin.
postfixadmin generate setup password
After creating the password hash, PostfixAdmin will display a line like below.
$CONF['setup_password'] = '$2y$10$58fIawuOb5y538RMBol/DOoqv2bJ7zhPRzRO.4Xq7MLeQJHmaFwF2';
You need to open the config.local.php file.
sudo nano /var/www/postfixadmin/config.local.php
Add the line displayed on PostfixAdmin setup page to the end of the file like below.
debian postfixadmin setup password
After saving the file, you need to refresh the PostfixAdmin setup page and enter the setup password again, then create the admin account. Please don’t use a Gmail, Yahoo Mail, or Microsoft email address for the admin account, or you might not be able to log in later. Use an email address on your own domain. You can create the email address later in PostfixAdmin.
postfixadmin create superadmin account
Once the superadmin account is created, you can log into PostfixAdmin at postfixadmin.example.com/login.php.
postfixadmin login page debian
Step 11: Configure Postfix to Use MySQL/MariaDB Database
By default, Postfix delivers emails only to users with a local Unix account. To make it deliver emails to virtual users whose information is stored in the database, we need to configure Postfix to use virtual mailbox domains.
First, we need to add MySQL map support for Postfix by installing the postfix-mysql package.
sudo apt install postfix-mysql
Then edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of this file.
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
virtual_alias_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
Where:
virtual_mailbox_domains points to a file that will tell Postfix how to look up domain information from the database.
virtual_mailbox_maps points to files that will tell Postfix how to look up email addresses from the database.
virtual_alias_maps points to files that will tell Postfix how to look up aliases from the database.
We want to use dovecot to deliver incoming emails to the virtual users’ message store, so also add the following line at the end of this file.
virtual_transport = lmtp:unix:private/dovecot-lmtp
Configure-Postfix-to-Use-MySQL-MariaDB-Database-ubuntu
Save and close the file. Next, we need to create the .cf files one by one. Create the sql directory.
sudo mkdir /etc/postfix/sql/
Create the mysql_virtual_domains_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_domains_maps.cf
Add the following content. Replace password with the postfixadmin password you set in Step 2.
user = postfixadmin
password = postfixadmin_password
hosts = localhost
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
#query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_mailbox_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_mailbox_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_alias_domain_mailbox_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
Create the mysql_virtual_alias_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_alias_domain_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_password
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Create the mysql_virtual_alias_domain_catchall_maps file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
Add the following content.
# handles catch-all settings of target-domain
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Since the database passwords are stored in plain text so they should be readable only by user postfix and root, which is done by executing the following two commands.
sudo chmod 0640 /etc/postfix/sql/*
sudo setfacl -R -m u:postfix:rx /etc/postfix/sql/
Next, we need to change the value of the mydestination parameter in Postfix. Display the current value:
sudo postconf mydestination
Sample output:
mydestination = $myhostname, linuxbabe.com, localhost.$mydomain, localhost
The mydestination parameter contains a list of domain names that will receive emails delivered to local Unix accounts. In part 1, we added the apex domain name (like linuxbabe.com) to mydestination. Since we are going to use virtual mailbox, we need to remove the apex domain name from the list by issuing the following command.
sudo postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost"
Now let’s open the Postfix main configuration file again.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of this file.
virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 2000
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
The first line defines the base location of mail files. The remaining 3 lines define which user ID and group ID Postfix will use when delivering incoming emails to the mailbox. We use the user ID 2000 and group ID 2000.
Save and close the file. Restart Postfix for the changes to take effect.
sudo systemctl restart postfix
Next, we need to create a user named vmail with ID 2000 and a group with ID 2000.
sudo adduser vmail --system --group --uid 2000 --disabled-login --no-create-home
Create the mail base location.
sudo mkdir /var/vmail/
Make vmail as the owner.
sudo chown vmail:vmail /var/vmail/ -R
Step 12: Configure Dovecot to Use MySQL/MariaDB Database
We also need to configure the Dovecot IMAP server to query user information from the database. First, run the following command to add MySQL support for Dovecot.
sudo apt install dovecot-mysql
Then edit the 10-mail.conf file.
sudo nano /etc/dovecot/conf.d/10-mail.conf
In part 2, we used the following mail_location. Email messages are stored under the Maildir directory under each user’s home directory.
mail_location = maildir:~/Maildir
Since we are using virtual mailbox domain now, we need to enable mail_home for the virtual users by adding the following line in the file, because virtual users don’t have home directories by default.
mail_home = /var/vmail/%d/%n/
virtual mailbox home directory
Save and close the file. Then edit the 10-auth.conf file.
sudo nano /etc/dovecot/conf.d/10-auth.conf
In part 2, we used the following value for auth_username_format.
auth_username_format = %n
The %n would drop the domain if it was given. Because in part 2 we were using local Unix account for the username of every email address, we must use %n to drop the domain, so users were able to login with the full email address.
Now we are using virtual mailbox domains, which means the username of every email address includes the domain part, so we need to change the auth_username_format as follows. %u won’t drop away the domain. This allows users to login with the full email address.
auth_username_format = %u
Uncomment the following line at the end of this file, so Dovecot can query user information from MySQL/MariaDB database.
!include auth-sql.conf.ext
Now you probably don’t want local Unix users to send emails without registering email addresses in PostfixAdmin, then comment out the following line by adding the # character at the beginning, so Dovecot won’t query the local /etc/passwd or /etc/shadow file.
#!include auth-system.conf.ext
It can be helpful to add the following two lines in this file to debug login issues. The login errors would be logged into /var/log/mail.log file. (Once users can login without problems, you can comment out the following two lines.)
auth_debug = yes
auth_debug_passwords = yes
dovecot mysql Password database
Save and close the file.
Edit the dovecot-sql.conf.ext file.
sudo nano /etc/dovecot/dovecot-sql.conf.ext
Here is the content that you should have in this file. By default, all lines in this file are commented out, so you can simply copy and paste them at the bottom. Replace postfixadmin_password with the postfixadmin password you set in Step 2.
driver = mysql
connect = host=localhost dbname=postfixadmin user=postfixadmin password=postfixadmin_password
default_pass_scheme = ARGON2I
password_query = SELECT username AS user,password FROM mailbox WHERE username = '%u' AND active='1'
user_query = SELECT maildir, 2000 AS uid, 2000 AS gid FROM mailbox WHERE username = '%u' AND active='1'
iterate_query = SELECT username AS user FROM mailbox
Restart Dovecot.
sudo systemctl restart dovecot
When a user tries to log in, Dovecot would use the Argon2 algorithm to generate a password hash from the password entered by the user, then compare it with the password hash stored in the database. If they match, then the user can log in successfully.
Step 13: Add Domain and Mailboxes in PostfixAdmin
Log in to PostfixAdmin web interface as the admin. Click the Domain List tab and select New Domain to add a domain. You can choose how many aliases and mailboxes are allowed for this domain.
debian postfixadmin add domain
Then click Virtual List tab and select Add Mailbox to add a new email address for your domain.
debian postfixadmin Create a new mailbox
Next, you can open your desktop email client such as Mozilla Thunderbird and add a mail account.
In the incoming server section, select IMAP protocol, enter mail.your-domain.com as the server name, choose port 143 and STARTTLS. Choose normal password as the authentication method.
In the outgoing section, select SMTP protocol, enter mail.your-domain.com as the server name, choose port 587 and STARTTLS. Choose normal password as the authentication method.
thunderbird-mail-client-configuration
Hint: You can also use port 993 with SSL/TLS encryption for IMAP, and use port 465 with SSL/TLS encryption for SMTP. You should not use port 25 as the SMTP port in mail clients to submit outgoing emails.
You should now be able to connect to your own email server and also send and receive emails with your desktop email client! Note that you cannot use local Unix accounts to login now. You must log in with the virtual user created from PostfixAdmin web interface.
Troubleshooting Tips
As a rule of thumb, you should always check the mail log (/var/log/mail.log) on your mail server when an error happens. The following is a list of specific errors and troubleshooting tips.
Can’t login from Mail Clients
If you can’t log into your mail server from a desktop mail client, scan your mail server to find if the ports are open. Note that you should run the following command from another Linux computer or server. If you run it on your mail server, then the ports will always appear to be open.
sudo nmap mail.your-domain.com
And check if Dovecot is running.
systemctl status dovecot
You can also check the mail log (/var/log/mail.log), which may give you some clues. If Dovecot fails to start, the error might not be logged to the /var/log/mail.log file, you can run the following command to see what’s wrong.
sudo journalctl -eu dovecot
If you see the following error in the mail log, it’s likely that you didn’t set a correct password in the .cf files under /etc/postfix/sql/ directory.
postfix/trivial-rewrite[28494]: warning: virtual_alias_domains: proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf: table lookup problem
postfix/trivial-rewrite[28494]: warning: virtual_alias_domains lookup failure
If you see the following error in the mail log, it’s because you forgot to add mail_location = maildir:~/Maildir in the /etc/dovecot/conf.d/10-mail.conf file.
open(/var/mail/username@domain.com) failed: Permission denied (euid=2000(vmail) egid=2000(vmail) missing +w perm: /var/mail, we're not in group 8(mail), dir owned by 0:8 mode=0775
Cloudflare DNS
As I said in part 1, if you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating DNS A record and AAAA record for the hostname of your mail server. Cloudflare doesn’t support SMTP or IMAP proxy.
Relay Access Denied
If you see the “relay access denied” error when trying to send emails from a mail client, it’s most likely that you use port 25 as the SMTP port in your mail client. As I said a while ago, you should use port 587 or 465 as the SMTP port in mail clients (Mozilla Thunberbird, Microsoft Outlook, etc) to submit outgoing emails. Port 25 should be used for SMTP server to SMTP server communications.
postfix dovecot relay access denied
iOS Mail App
If you use the iOS Mail app to log into your mail server and encounter the following error.
ios the mail server is not responding
You can try to fix it by enforcing SSL encryption, for both SMTP and IMAP.
ios mail enforce SSL encryption
Fun fact: It seems the iOS Mail app has difficulty in supporting STARTTLS on IMAP port 143, but it supports STARTTLS on the submission port 587.
Temporary Lookup Failure
If your mail server was working fine for some time, but suddenly you find the following error in the mail log,
Aug 25 20:25:24 mx postfix/trivial-rewrite[3313]: warning: virtual_alias_domains: proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf: table lookup problem
Aug 25 20:25:24 mx postfix/trivial-rewrite[3313]: warning: virtual_alias_domains lookup failure
Aug 25 20:25:24 mx postfix/submission/smtpd[3464]: NOQUEUE: reject: 451 4.3.0 <user@your-domain.com>: Temporary lookup failure; proto=ESMTP
Aug 25 20:25:24 mx postfix/submission/smtpd[3464]: Temporary lookup failure
It’s likely that your MariaDB/MySQL database stopped somehow. You can use the following command to check when your database server stopped.
sudo journalctl -eu mariadb
or
sudo journalctl -eu mysql
A common cause for this situation is that your server is out-of-memory. Check if your server has enough memory.
htop
or
free -m
Automatically Clean the Junk Folder and Trash Folder
To delete emails in Junk folder for all users, you can run
sudo doveadm expunge -A mailbox Junk all
To delete emails in Trash folder, run
sudo doveadm expunge -A mailbox Trash all
I think it’s better to clean emails that have been in the Junk or Trash folder for more than 2 weeks, instead of cleaning all emails.
sudo doveadm expunge -A mailbox Junk savedbefore 2w
Then add a cron job to automate the job.
sudo crontab -e
Add the following line to clean Junk and Trash folder every day.
@daily doveadm expunge -A mailbox Junk savedbefore 2w;doveadm expunge -A mailbox Trash savedbefore 2w
To receive report when a Cron job produces an error, you can add the following line above all Cron jobs.
MAILTO="you@your-domain.com"
Save and close the file. And you’re done.
Change User Password in PostfixAdmin
Users can log into PostfixAdmin at https://postfixadmin.example.com/users/login.php, then change their passwords.
Restricting Access to Sendmail
By default, any local user can use the sendmail binary to submit outgoing emails. Now that your mail server is using virtual mailboxes, you might want to restrict access to the sendmail binary to trusted local users only, so a malicious user can’t use it to send a large volume of emails to damage your mail server’s reputation. Edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following line to the end of this file, so only the root and www-data user can submit emails via sendmail. You can also add other usernames.
authorized_submit_users = root,www-data
Save and close the file. Then restart Postfix.
sudo systemctl restart postfix
How to Upgrade PostfixAdmin
Sometimes there might be bugs in the current PostfixAdmin that can be fixed by upgrading to a new version. It’s very simple to upgrade PostfixAdmin. When a new version of PostfixAdmin comes out, then you need to
Make a copy of the /var/www/postfixadmin/config.local.php file to your home directory.
Remove the PostfixAdmin web directory (sudo rm /var/www/postfixadmin/ -r)
Go through step 2, step 3, step 5, and step 10 again in this tutorial. When doing step 5, simply copy the config.local.php file from your home directory. In step 10, the PostfixAdmin database schema will be automatically updated when you visit the setup.php web page.
I once had a login loop problem in my PostfixAdmin installation and it’s fixed by upgrading PostfixAdmin to the latest version.
Next Step
I hope this tutorial helped you install and use PostfixAdmin on Debian to create virtual mailboxes. In part 4, I will show you how to set up SPF and DKIM with Postfix to improve email deliverability and in a future tutorial, I’m going to show you how to host multiple domains with PostfixAdmin.
If you want to access emails from a web browser, then I recommend Roundcube, which is a very popular and featured-rich open-source webmail client. As always, if you found this post useful, subscribe to our free newsletter to get more tips and tricks. Take care
With virtual mailboxes, we don’t need to create local Unix account for each email address. If you are going to set up a mail server for a company or organization, it’s always better to have an easy way to create virtual mailboxes in a web-based interface, which also allows users to change their passwords. That’s where PostfixAdmin comes in.
PostfixAdmin Features
manage mailboxes, virtual domains, and aliases
vacation/out-of-office messages (Personally I think this feature is better done in Roundcube Webmail.)
alias domains (forwarding one domain to another with recipient validation)
users can manage their own mailbox (change alias, password, and vacation message)
quota support for single mailboxes and total quota of a domain
fetchmail integration: You can fetch emails from your original email address to your new email address.
command line client postfixadmin-cli for those who don’t want to click around in a web interface
Note: Once you finish part 3, you can no longer use local Unix accounts as email addresses. You must create email addresses from the PostfixAdmin web interface.
Requirements
I assume that you have followed part 1 and part 2 of this tutorial series. If you followed mail server tutorials on other websites, I recommend purging your configurations (sudo apt purge postfix dovecot-core) and start over with my tutorial series, so you are not going to be confused by different setup processes.
Once the above requirements are met, let’s install and configure PostfixAdmin.
Step 1: Install MariaDB Database Server
PostfixAdmin is written in PHP and requires a database (MySQL/MariaDB, PostgreSQL or SQLite). This article will use MariaDB database, which is a drop-in replacement for MySQL. It is developed by former members of MySQL team who are concerned that Oracle might turn MySQL into a closed-source product. Enter the following commands to install MariaDB on Debian.
sudo apt update
sudo apt install mariadb-server mariadb-client
After it’s installed, MariaDB server should be automatically started. Use systemctl to check its status.
systemctl status mariadb
Sample output:
● mariadb.service - MariaDB 10.5.12 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2021-10-29 23:44:59 EDT; 1min 12s ago
Docs: man:mariadbd(8)
https://mariadb.com/kb/en/library/systemd/
Process: 435260 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)
Process: 435261 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
Process: 435263 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] && VAR= || VAR=`cd /usr/bin/..; /usr/bin/galera_recovery`; [ $? -eq 0 ] && systemctl set-environment _WSREP_START_POSITIO>
Process: 435322 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
Process: 435324 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)
Main PID: 435310 (mariadbd)
Status: "Taking your SQL requests now..."
Tasks: 9 (limit: 1095)
Memory: 78.7M
CPU: 741ms
CGroup: /system.slice/mariadb.service
└─435310 /usr/sbin/mariadbd
If it’s not running, start it with this command:
sudo systemctl start mariadb
To enable MariaDB to automatically start at boot time, run
sudo systemctl enable mariadb
Now run the post-installation security script.
sudo mysql_secure_installation
When it asks you to enter MariaDB root password, press Enter key as the root password isn’t set yet. Your root account is already secured, so you should answer n to not set password.
debian bullseye secure mariadb
Next, you can press Enter to answer all remaining questions, which will remove anonymous user, disable remote root login and remove test database. This step is a basic requirement for MariaDB database security. (Notice that Y is capitalized, which means it is the default answer. )
install mariadb debian bullseye
Step 2: Download PostfixAdmin on Debian Server
PostfixAdmin is included in the default Debian repository. However, I don’t recommend it, as it can create problems when you upgrade the Debian system to a new version. So I will show you how to install the latest version of PostfixAdmin. Go to PostfixAdmin Gitbub page to download the latest version. You can use the wget tool to download it from command line. The download link is always available in the format below. If a new version comes out, simply replace 3.3.10 with the new version number.
sudo apt install wget
wget https://github.com/postfixadmin/postfix ... .10.tar.gz
Once downloaded, extract the archive to the /var/www/ directory and rename it to postfixadmin.
sudo mkdir -p /var/www/
sudo tar xvf postfixadmin-3.3.10.tar.gz -C /var/www/
sudo mv /var/www/postfixadmin-postfixadmin-3.3.10 /var/www/postfixadmin
Step 3: Setting Up Permissions
PostfixAdmin requires a templates_c directory, and the web server needs read and write access to this directory, so run the following commands.
sudo mkdir /var/www/postfixadmin/templates_c
sudo apt install acl
sudo setfacl -R -m u:www-data:rwx /var/www/postfixadmin/templates_c/
Starting with Dovecot 2.3.11, the web server user needs permission to read Let’s Encrypt TLS certificate in order to do password hashing. Run the following two commands to grant permissions.
sudo setfacl -R -m u:www-data:rx /etc/letsencrypt/live/ /etc/letsencrypt/archive/
Step 4: Create a Database and User for PostfixAdmin
Log into MySQL/MariaDB shell as root with the following command.
sudo mysql -u root
Once you are logged in, create a database for PostfixAdmin using the following command. I named it postfixadmin, but you can use whatever name you like. (Don’t leave out the semicolon.)
create database postfixadmin;
Then enter the command below to create a database user for PostfixAdmin. This command also grants all privileges of postfixadmin database to the user. Replace postfixadmin_password with your preferred password. Note that the password should not contain the # character, or you might not be able to log in later.
grant all privileges on postfixadmin.* to 'postfixadmin'@'localhost' identified by 'postfixadmin_password';
Flush the privileges table for the changes to take effect and then get out of MariaDB shell.
flush privileges;
exit;
Step 5: Configure PostfixAdmin
The default PostfixAdmin configuration file is config.inc.php. We need to create a config.local.php file and add custom configurations.
sudo nano /var/www/postfixadmin/config.local.php
Add the following lines in the file, so PostfixAdmin can connect to MySQL/MariaDB database. Replace postfixadmin_password with the real PostfixAdmin password created in step 4.
<?php
$CONF['configured'] = true;
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_port'] = '3306';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'postfixadmin_password';
$CONF['database_name'] = 'postfixadmin';
$CONF['encrypt'] = 'dovecot:ARGON2I';
$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5";
if(@file_exists('/usr/bin/doveadm')) { // @ to silence openbase_dir stuff; see https://github.com/postfixadmin/postfixadmin/issues/171
$CONF['dovecotpw'] = "/usr/bin/doveadm pw -r 5"; # debian
}
Save and close the file. Note that we will use the ARGON2I password scheme.
Step 6: Create Apache Virtual Host or Nginx Config File for PostfixAdmin
Apache
If you use Apache web server, create a virtual host for PostfixAdmin.
sudo nano /etc/apache2/sites-available/postfixadmin.conf
Put the following text into the file. Replace postfixadmin.example.com with your real domain name and don’t forget to set DNS A record for it.
<VirtualHost *:80>
ServerName postfixadmin.example.com
DocumentRoot /var/www/postfixadmin/public
ErrorLog ${APACHE_LOG_DIR}/postfixadmin_error.log
CustomLog ${APACHE_LOG_DIR}/postfixadmin_access.log combined
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/postfixadmin/>
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
</VirtualHost>
Save and close the file. Then enable this virtual host with:
sudo a2ensite postfixadmin.conf
Reload Apache for the changes to take effect.
sudo systemctl reload apache2
Now you should be able to see the PostfixAdmin web-based install wizard at http://postfixadmin.example.com/setup.php.
Nginx
If you use Nginx web server, create a virtual host for PostfixAdmin.
sudo nano /etc/nginx/conf.d/postfixadmin.conf
Put the following text into the file. Replace postfixadmin.example.com with your real domain name and don’t forget to set DNS A record for it.
server {
listen 80;
listen [::]:80;
server_name postfixadmin.example.com;
root /var/www/postfixadmin/public/;
index index.php index.html;
access_log /var/log/nginx/postfixadmin_access.log;
error_log /var/log/nginx/postfixadmin_error.log;
location / {
try_files $uri $uri/ /index.php;
}
location ~ ^/(.+\.php)$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
Note: Debian 11 ships PHP7.4 and Debian 10 ships with PHP7.3. If you use Debian 10, then change php7.4-fpm to php7.3-fpm in the above file.
Save and close the file. Then test Nginx configuration.
sudo nginx -t
If the test is successful, reload Nginx for the changes to take effect.
sudo systemctl reload nginx
Now you should be able to see the PostfixAdmin web-based install wizard at http://postfixadmin.example.com/setup.php.
Step 7: Install Required and Recommended PHP Modules
Run the following command to install PHP modules required or recommended by PostfixAdmin on Debian 11.
sudo apt install php7.4-fpm php7.4-imap php7.4-mbstring php7.4-mysql php7.4-json php7.4-curl php7.4-zip php7.4-xml php7.4-bz2 php7.4-intl php7.4-gmp
If you use Debian 10, run the following command.
sudo apt install php7.3-fpm php7.3-imap php7.3-mbstring php7.3-mysql php7.3-json php7.3-curl php7.3-zip php7.3-xml php7.3-bz2 php7.3-intl php7.3-gmp
Then restart Apache. (If you use Nginx, you don’t need to restart Nginx.)
sudo systemctl restart apache2
Step 8: Enabling HTTPS
To encrypt the HTTP traffic, we can enable HTTPS by installing a free TLS certificate issued from Let’s Encrypt. Run the following command to install Let’s Encrypt client (certbot) on Debian server.
sudo apt install certbot
If you use Apache, install the Certbot Apache plugin.
sudo apt install python3-certbot-apache
And run this command to obtain and install TLS certificate.
sudo certbot --apache --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d postfixadmin.example.com
If you use Nginx, then you also need to install the Certbot Nginx plugin.
sudo apt install python3-certbot-nginx
Next, run the following command to obtain and install TLS certificate.
sudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp --email you@example.com -d postfixadmin.example.com
Where
--nginx: Use the nginx plugin.
--apache: Use the Apache plugin.
--agree-tos: Agree to terms of service.
--redirect: Force HTTPS by 301 redirect.
--hsts: Add the Strict-Transport-Security header to every HTTP response. Forcing browser to always use TLS for the domain. Defends against SSL/TLS Stripping.
--staple-ocsp: Enables OCSP Stapling. A valid OCSP response is stapled to the certificate that the server offers during TLS.
The certificate should now be obtained and automatically installed, which is indicated by the message below.
postfixadmin ubuntu https
Step 9: Enable Statistics in Dovecot
PostfixAdmin needs to read Dovecot statistics. Edit the Dovecot configuration file.
sudo nano /etc/dovecot/conf.d/10-master.conf
Add the following lines to the end of this file.
service stats {
unix_listener stats-reader {
user = www-data
group = www-data
mode = 0660
}
unix_listener stats-writer {
user = www-data
group = www-data
mode = 0660
}
}
Save and close the file. Then add the web server to the dovecot group.
sudo gpasswd -a www-data dovecot
Restart Dovecot.
sudo systemctl restart dovecot
Step 10: Finish the Installation in Web Browser
Go to postfixadmin.example.com/setup.php to run the web-based setup wizard. First, you need to create a setup password for PostfixAdmin.
postfixadmin generate setup password
After creating the password hash, PostfixAdmin will display a line like below.
$CONF['setup_password'] = '$2y$10$58fIawuOb5y538RMBol/DOoqv2bJ7zhPRzRO.4Xq7MLeQJHmaFwF2';
You need to open the config.local.php file.
sudo nano /var/www/postfixadmin/config.local.php
Add the line displayed on PostfixAdmin setup page to the end of the file like below.
debian postfixadmin setup password
After saving the file, you need to refresh the PostfixAdmin setup page and enter the setup password again, then create the admin account. Please don’t use a Gmail, Yahoo Mail, or Microsoft email address for the admin account, or you might not be able to log in later. Use an email address on your own domain. You can create the email address later in PostfixAdmin.
postfixadmin create superadmin account
Once the superadmin account is created, you can log into PostfixAdmin at postfixadmin.example.com/login.php.
postfixadmin login page debian
Step 11: Configure Postfix to Use MySQL/MariaDB Database
By default, Postfix delivers emails only to users with a local Unix account. To make it deliver emails to virtual users whose information is stored in the database, we need to configure Postfix to use virtual mailbox domains.
First, we need to add MySQL map support for Postfix by installing the postfix-mysql package.
sudo apt install postfix-mysql
Then edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of this file.
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_mailbox_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
virtual_alias_maps =
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf,
proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
Where:
virtual_mailbox_domains points to a file that will tell Postfix how to look up domain information from the database.
virtual_mailbox_maps points to files that will tell Postfix how to look up email addresses from the database.
virtual_alias_maps points to files that will tell Postfix how to look up aliases from the database.
We want to use dovecot to deliver incoming emails to the virtual users’ message store, so also add the following line at the end of this file.
virtual_transport = lmtp:unix:private/dovecot-lmtp
Configure-Postfix-to-Use-MySQL-MariaDB-Database-ubuntu
Save and close the file. Next, we need to create the .cf files one by one. Create the sql directory.
sudo mkdir /etc/postfix/sql/
Create the mysql_virtual_domains_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_domains_maps.cf
Add the following content. Replace password with the postfixadmin password you set in Step 2.
user = postfixadmin
password = postfixadmin_password
hosts = localhost
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
#query = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_mailbox_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_mailbox_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_alias_domain_mailbox_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
Create the mysql_virtual_alias_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
#expansion_limit = 100
Create the mysql_virtual_alias_domain_maps.cf file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_maps.cf
Add the following content.
user = postfixadmin
password = postfixadmin_password
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Create the mysql_virtual_alias_domain_catchall_maps file.
sudo nano /etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
Add the following content.
# handles catch-all settings of target-domain
user = postfixadmin
password = postfixadmin_assword
hosts = localhost
dbname = postfixadmin
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
Since the database passwords are stored in plain text so they should be readable only by user postfix and root, which is done by executing the following two commands.
sudo chmod 0640 /etc/postfix/sql/*
sudo setfacl -R -m u:postfix:rx /etc/postfix/sql/
Next, we need to change the value of the mydestination parameter in Postfix. Display the current value:
sudo postconf mydestination
Sample output:
mydestination = $myhostname, linuxbabe.com, localhost.$mydomain, localhost
The mydestination parameter contains a list of domain names that will receive emails delivered to local Unix accounts. In part 1, we added the apex domain name (like linuxbabe.com) to mydestination. Since we are going to use virtual mailbox, we need to remove the apex domain name from the list by issuing the following command.
sudo postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost"
Now let’s open the Postfix main configuration file again.
sudo nano /etc/postfix/main.cf
Add the following lines at the end of this file.
virtual_mailbox_base = /var/vmail
virtual_minimum_uid = 2000
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
The first line defines the base location of mail files. The remaining 3 lines define which user ID and group ID Postfix will use when delivering incoming emails to the mailbox. We use the user ID 2000 and group ID 2000.
Save and close the file. Restart Postfix for the changes to take effect.
sudo systemctl restart postfix
Next, we need to create a user named vmail with ID 2000 and a group with ID 2000.
sudo adduser vmail --system --group --uid 2000 --disabled-login --no-create-home
Create the mail base location.
sudo mkdir /var/vmail/
Make vmail as the owner.
sudo chown vmail:vmail /var/vmail/ -R
Step 12: Configure Dovecot to Use MySQL/MariaDB Database
We also need to configure the Dovecot IMAP server to query user information from the database. First, run the following command to add MySQL support for Dovecot.
sudo apt install dovecot-mysql
Then edit the 10-mail.conf file.
sudo nano /etc/dovecot/conf.d/10-mail.conf
In part 2, we used the following mail_location. Email messages are stored under the Maildir directory under each user’s home directory.
mail_location = maildir:~/Maildir
Since we are using virtual mailbox domain now, we need to enable mail_home for the virtual users by adding the following line in the file, because virtual users don’t have home directories by default.
mail_home = /var/vmail/%d/%n/
virtual mailbox home directory
Save and close the file. Then edit the 10-auth.conf file.
sudo nano /etc/dovecot/conf.d/10-auth.conf
In part 2, we used the following value for auth_username_format.
auth_username_format = %n
The %n would drop the domain if it was given. Because in part 2 we were using local Unix account for the username of every email address, we must use %n to drop the domain, so users were able to login with the full email address.
Now we are using virtual mailbox domains, which means the username of every email address includes the domain part, so we need to change the auth_username_format as follows. %u won’t drop away the domain. This allows users to login with the full email address.
auth_username_format = %u
Uncomment the following line at the end of this file, so Dovecot can query user information from MySQL/MariaDB database.
!include auth-sql.conf.ext
Now you probably don’t want local Unix users to send emails without registering email addresses in PostfixAdmin, then comment out the following line by adding the # character at the beginning, so Dovecot won’t query the local /etc/passwd or /etc/shadow file.
#!include auth-system.conf.ext
It can be helpful to add the following two lines in this file to debug login issues. The login errors would be logged into /var/log/mail.log file. (Once users can login without problems, you can comment out the following two lines.)
auth_debug = yes
auth_debug_passwords = yes
dovecot mysql Password database
Save and close the file.
Edit the dovecot-sql.conf.ext file.
sudo nano /etc/dovecot/dovecot-sql.conf.ext
Here is the content that you should have in this file. By default, all lines in this file are commented out, so you can simply copy and paste them at the bottom. Replace postfixadmin_password with the postfixadmin password you set in Step 2.
driver = mysql
connect = host=localhost dbname=postfixadmin user=postfixadmin password=postfixadmin_password
default_pass_scheme = ARGON2I
password_query = SELECT username AS user,password FROM mailbox WHERE username = '%u' AND active='1'
user_query = SELECT maildir, 2000 AS uid, 2000 AS gid FROM mailbox WHERE username = '%u' AND active='1'
iterate_query = SELECT username AS user FROM mailbox
Restart Dovecot.
sudo systemctl restart dovecot
When a user tries to log in, Dovecot would use the Argon2 algorithm to generate a password hash from the password entered by the user, then compare it with the password hash stored in the database. If they match, then the user can log in successfully.
Step 13: Add Domain and Mailboxes in PostfixAdmin
Log in to PostfixAdmin web interface as the admin. Click the Domain List tab and select New Domain to add a domain. You can choose how many aliases and mailboxes are allowed for this domain.
debian postfixadmin add domain
Then click Virtual List tab and select Add Mailbox to add a new email address for your domain.
debian postfixadmin Create a new mailbox
Next, you can open your desktop email client such as Mozilla Thunderbird and add a mail account.
In the incoming server section, select IMAP protocol, enter mail.your-domain.com as the server name, choose port 143 and STARTTLS. Choose normal password as the authentication method.
In the outgoing section, select SMTP protocol, enter mail.your-domain.com as the server name, choose port 587 and STARTTLS. Choose normal password as the authentication method.
thunderbird-mail-client-configuration
Hint: You can also use port 993 with SSL/TLS encryption for IMAP, and use port 465 with SSL/TLS encryption for SMTP. You should not use port 25 as the SMTP port in mail clients to submit outgoing emails.
You should now be able to connect to your own email server and also send and receive emails with your desktop email client! Note that you cannot use local Unix accounts to login now. You must log in with the virtual user created from PostfixAdmin web interface.
Troubleshooting Tips
As a rule of thumb, you should always check the mail log (/var/log/mail.log) on your mail server when an error happens. The following is a list of specific errors and troubleshooting tips.
Can’t login from Mail Clients
If you can’t log into your mail server from a desktop mail client, scan your mail server to find if the ports are open. Note that you should run the following command from another Linux computer or server. If you run it on your mail server, then the ports will always appear to be open.
sudo nmap mail.your-domain.com
And check if Dovecot is running.
systemctl status dovecot
You can also check the mail log (/var/log/mail.log), which may give you some clues. If Dovecot fails to start, the error might not be logged to the /var/log/mail.log file, you can run the following command to see what’s wrong.
sudo journalctl -eu dovecot
If you see the following error in the mail log, it’s likely that you didn’t set a correct password in the .cf files under /etc/postfix/sql/ directory.
postfix/trivial-rewrite[28494]: warning: virtual_alias_domains: proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf: table lookup problem
postfix/trivial-rewrite[28494]: warning: virtual_alias_domains lookup failure
If you see the following error in the mail log, it’s because you forgot to add mail_location = maildir:~/Maildir in the /etc/dovecot/conf.d/10-mail.conf file.
open(/var/mail/username@domain.com) failed: Permission denied (euid=2000(vmail) egid=2000(vmail) missing +w perm: /var/mail, we're not in group 8(mail), dir owned by 0:8 mode=0775
Cloudflare DNS
As I said in part 1, if you use Cloudflare DNS service, you should not enable the CDN (proxy) feature when creating DNS A record and AAAA record for the hostname of your mail server. Cloudflare doesn’t support SMTP or IMAP proxy.
Relay Access Denied
If you see the “relay access denied” error when trying to send emails from a mail client, it’s most likely that you use port 25 as the SMTP port in your mail client. As I said a while ago, you should use port 587 or 465 as the SMTP port in mail clients (Mozilla Thunberbird, Microsoft Outlook, etc) to submit outgoing emails. Port 25 should be used for SMTP server to SMTP server communications.
postfix dovecot relay access denied
iOS Mail App
If you use the iOS Mail app to log into your mail server and encounter the following error.
ios the mail server is not responding
You can try to fix it by enforcing SSL encryption, for both SMTP and IMAP.
ios mail enforce SSL encryption
Fun fact: It seems the iOS Mail app has difficulty in supporting STARTTLS on IMAP port 143, but it supports STARTTLS on the submission port 587.
Temporary Lookup Failure
If your mail server was working fine for some time, but suddenly you find the following error in the mail log,
Aug 25 20:25:24 mx postfix/trivial-rewrite[3313]: warning: virtual_alias_domains: proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf: table lookup problem
Aug 25 20:25:24 mx postfix/trivial-rewrite[3313]: warning: virtual_alias_domains lookup failure
Aug 25 20:25:24 mx postfix/submission/smtpd[3464]: NOQUEUE: reject: 451 4.3.0 <user@your-domain.com>: Temporary lookup failure; proto=ESMTP
Aug 25 20:25:24 mx postfix/submission/smtpd[3464]: Temporary lookup failure
It’s likely that your MariaDB/MySQL database stopped somehow. You can use the following command to check when your database server stopped.
sudo journalctl -eu mariadb
or
sudo journalctl -eu mysql
A common cause for this situation is that your server is out-of-memory. Check if your server has enough memory.
htop
or
free -m
Automatically Clean the Junk Folder and Trash Folder
To delete emails in Junk folder for all users, you can run
sudo doveadm expunge -A mailbox Junk all
To delete emails in Trash folder, run
sudo doveadm expunge -A mailbox Trash all
I think it’s better to clean emails that have been in the Junk or Trash folder for more than 2 weeks, instead of cleaning all emails.
sudo doveadm expunge -A mailbox Junk savedbefore 2w
Then add a cron job to automate the job.
sudo crontab -e
Add the following line to clean Junk and Trash folder every day.
@daily doveadm expunge -A mailbox Junk savedbefore 2w;doveadm expunge -A mailbox Trash savedbefore 2w
To receive report when a Cron job produces an error, you can add the following line above all Cron jobs.
MAILTO="you@your-domain.com"
Save and close the file. And you’re done.
Change User Password in PostfixAdmin
Users can log into PostfixAdmin at https://postfixadmin.example.com/users/login.php, then change their passwords.
Restricting Access to Sendmail
By default, any local user can use the sendmail binary to submit outgoing emails. Now that your mail server is using virtual mailboxes, you might want to restrict access to the sendmail binary to trusted local users only, so a malicious user can’t use it to send a large volume of emails to damage your mail server’s reputation. Edit the Postfix main configuration file.
sudo nano /etc/postfix/main.cf
Add the following line to the end of this file, so only the root and www-data user can submit emails via sendmail. You can also add other usernames.
authorized_submit_users = root,www-data
Save and close the file. Then restart Postfix.
sudo systemctl restart postfix
How to Upgrade PostfixAdmin
Sometimes there might be bugs in the current PostfixAdmin that can be fixed by upgrading to a new version. It’s very simple to upgrade PostfixAdmin. When a new version of PostfixAdmin comes out, then you need to
Make a copy of the /var/www/postfixadmin/config.local.php file to your home directory.
Remove the PostfixAdmin web directory (sudo rm /var/www/postfixadmin/ -r)
Go through step 2, step 3, step 5, and step 10 again in this tutorial. When doing step 5, simply copy the config.local.php file from your home directory. In step 10, the PostfixAdmin database schema will be automatically updated when you visit the setup.php web page.
I once had a login loop problem in my PostfixAdmin installation and it’s fixed by upgrading PostfixAdmin to the latest version.
Next Step
I hope this tutorial helped you install and use PostfixAdmin on Debian to create virtual mailboxes. In part 4, I will show you how to set up SPF and DKIM with Postfix to improve email deliverability and in a future tutorial, I’m going to show you how to host multiple domains with PostfixAdmin.
If you want to access emails from a web browser, then I recommend Roundcube, which is a very popular and featured-rich open-source webmail client. As always, if you found this post useful, subscribe to our free newsletter to get more tips and tricks. Take care