Professional Documents
Culture Documents
Contents
Installation
Configuration
User
Database
PostfixAdmin
SSL certificate
Postfix
Setting up Postfix
Create the file structure
Dovecot
DH parameters
PostfixAdmin
Roundcube
Apache configuration
Roundcube: Change Password Plugin
Fire it up
Testing
Error response
See that you have received a email
Optional Items
Quota
Autocreate and autosubscribe folders in Dovecot
Dovecot public folder and global ACLs
Fighting Spam
Sidenotes
Alternative vmail folder structure
Troubleshooting
IMAP/POP3 client failing to receive mails
Roundcube not able to delete emails or view any 'standard' folders
LMTP / Sieve
Are your emails sent to gmail users ending up in their junk/spam folders?
See also
Installation
Before you start, you must have both a working MySQL server as described in MySQL and a
working Postfix server as described in Postfix.
Configuration
User
For security reasons, a new user should be created to store the mails:
A gid and uid of 5000 is used in both cases so that we do not run into conflicts with regular users.
All your mail will then be stored in /home/vmail . You could change the home directory to
something like /var/mail/vmail but be careful to change this in any configuration below as
well.
Database
You will need to create an empty database and corresponding user. In this article, the user
postfix_user will have read/write access to the database postfix_db using hunter2 as password.
You are expected to create the database and user yourself, and give the user permission to use the
database, as shown in the following code.
$ mysql -u root -p
FLUSH PRIVILEGES;
Now you can go to the PostfixAdmin's setup page, let PostfixAdmin create the needed tables and
create the users in there.
PostfixAdmin
See PostfixAdmin.
SSL certificate
You will need a SSL certificate for all encrypted mail communications (SMTPS/IMAPS/POP3S). If
you do not have one, create one:
# cd /etc/ssl/private/
# openssl req -new -x509 -nodes -newkey rsa:4096 -keyout vmail.key -out vmail.crt -days 1460 #days are optional
Alternatively, create a free trusted certificate using Let's Encrypt. The private key will be in
/etc/letsencrypt/live/yourdomain/privkey.pem , the certificate in
/etc/letsencrypt/live/yourdomain/fullchain.pem . Either change the configuration
accordingly, or symlink the keys to /etc/ssl/private :
# ln -s /etc/letsencrypt/live/yourdomain/privkey.pem /etc/ssl/private/vmail.key
# ln -s /etc/letsencrypt/live/yourdomain/fullchain.pem /etc/ssl/private/vmail.crt
Postfix
Before you copy & paste the configuration below, check if relay_domains has already been set.
If you leave more than one active, you will receive warnings during runtime.
Warning: relay_domains can be dangerous. You usually do not want Postfix to forward mail
of strangers. $mydestination is a sane default value. Double check its value before running
postfix! See
http://www.postfix.org/BASIC_CONFIGURATION_README.html#relay_to
Also follow Postfix#Secure SMTP (receiving) pointing to the files you created in #SSL
certificate.
Setting up Postfix
To /etc/postfix/main.cf append:
relay_domains = $mydestination
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias_maps.cf
virtual_mailbox_domains = proxy:mysql:/etc/postfix/virtual_mailbox_domains.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/virtual_mailbox_maps.cf
virtual_mailbox_base = /home/vmail
virtual_mailbox_limit = 512000000
virtual_minimum_uid = 5000
virtual_transport = virtual
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
local_transport = virtual
local_recipient_maps = $virtual_mailbox_maps
transport_maps = hash:/etc/postfix/transport
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = /var/run/dovecot/auth-client
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_received_header = yes
smtpd_tls_cert_file = /etc/ssl/private/vmail.crt
smtpd_tls_key_file = /etc/ssl/private/vmail.key
smtpd_sasl_local_domain = $mydomain
smtpd_tls_loglevel = 1
smtp_tls_security_level = may
smtp_tls_loglevel = 1
In the configuration above virtual_mailbox_domains is a list of the domains that you want
to receive mail for. This CANNOT contain the domain that is set in mydestination . That is
why we left mydestination to be localhost only.
virtual_mailbox_maps will contain the information of virtual users and their mailbox
locations. We are using a hash file to store the more permanent maps, and these will then
override the forwards in the MySQL database.
virtual_mailbox_base is the base directory where the virtual mailboxes will be stored.
The virtual_uid_maps and virtual_gid_maps are the real system user IDs that the virtual
mails will be owned by. This is for storage purposes.
Note: Since we will be using a web interface (Roundcube), and do not want people accessing
this by any other means, we will be creating this account later without providing any login
access.
Those new additional settings reference a lot of files that do not even exist yet. We will create them
with the following steps.
If you were setting up your database with PostfixAdmin and created the database schema through
PostfixAdmin, you can create the following files. Do not forget to change the password:
/etc/postfix/virtual_alias_maps.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = alias
select_field = goto
where_field = address
/etc/postfix/virtual_mailbox_domains.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = domain
select_field = domain
where_field = domain
/etc/postfix/virtual_mailbox_maps.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = mailbox
select_field = maildir
where_field = username
/etc/postfix/main.cf
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias_maps.cf,proxy:mysql:/etc/postfix/virtual_alias_domains
_maps.cf
virtual_alias_domains = proxy:mysql:/etc/postfix/virtual_alias_domains.cf
/etc/postfix/virtual_alias_domains_maps.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
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'
/etc/postfix/virtual_alias_domains.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
query = SELECT alias_domain FROM alias_domain WHERE alias_domain='%s' AND active = '1'
Note: For setups without using PostfixAdmin, create the following files.
/etc/postfix/virtual_alias_maps.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = domains
select_field = virtual
where_field = domain
/etc/postfix/virtual_mailbox_domains.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = forwardings
select_field = destination
where_field = source
/etc/postfix/virtual_mailbox_maps.cf
user = postfix_user
password = hunter2
hosts = localhost
dbname = postfix_db
table = users
select_field = concat(domain,'/',email,'/')
where_field = email
# postmap /etc/postfix/transport
Dovecot
Instead of using the provided Dovecot example configuration file, we will create our own
/etc/dovecot/dovecot.conf . Please note that the user and group here might be vmail
instead of postfix!
/etc/dovecot/dovecot.conf
auth_mechanisms = plain
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf
service auth {
unix_listener auth-client {
group = postfix
mode = 0660
user = postfix
user = root
mail_home = /home/vmail/%d/%n
mail_location = maildir:~
ssl_cert = </etc/ssl/private/vmail.crt
ssl_key = </etc/ssl/private/vmail.key
Note: If you instead want to modify dovecot.conf.sample , beware that the default
configuration file imports the content of conf.d/*.conf . Those files call other files that are not
present in our configuration.
/etc/dovecot/dovecot-sql.conf
driver = mysql
default_pass_scheme = SHA512-CRYPT
user_query = SELECT '/home/vmail/%d/%n' as home, 'maildir:/home/vmail/%d/%n' as mail, 5000 AS uid, 5000 AS gid, co
ncat('dirsize:storage=', quota) AS quota FROM mailbox WHERE username = '%u' AND active = '1'
# If using client certificates for authentication, comment the above and uncomment the following
/etc/dovecot/dovecot-sql.conf
driver = mysql
default_pass_scheme = SHA512-CRYPT
user_query = SELECT '/home/vmail/%d/%n' as home, 'maildir:/home/vmail/%d/%n' as mail, 5000 AS uid, 5000 AS gid, co
ncat('dirsize:storage=', quota) AS quota FROM users WHERE email = '%u'
# If using client certificates for authentication, comment the above and uncomment the following
DH parameters
ssl_dh = /etc/dovecot/dh.pem
PostfixAdmin
See PostfixAdmin.
Note: To match the configuration in this file, config.inc.php should contain the following.
# /etc/webapps/postfixadmin/config.inc.php
...
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';
...
Roundcube
See Roundcube.
Make sure that both extension=pdo_mysql and extension=iconv are uncommented in your
php.ini file. Also check the .htaccess for access restrictions. Assuming that localhost is your
current host, navigate a browser to http://localhost/roundcube/installer/ and follow the
instructions.
Roundcube needs a separate database to work. You should not use the same database for
Roundcube and PostfixAdmin. Create a second database roundcube_db and a new user named
roundcube_user .
For the address of the IMAP host, i.e. default_host , use ssl://localhost/ or
tls://localhost/ and not just localhost .
Use port 993 . Likewise with SMTP.
For the address of the SMTP host, i.e. smtp_server , use tls://localhost/ and port
587 if you used STARTTLS. Use ssl://localhost/ with port 465 if you used SMTPS. If
there is a failure to establish a session, try using tls://yourservername instead, replacing
yourservername with the name of your server.
See #Postfix for an explanation on that.
Make sure the resulting configuration file has $config['smtp_user'] = '%u'; and
$config['smtp_pass'] = '%p'; lines in it or you will not be able to send email.
The post install process is similar to any other webapp like PhpMyAdmin or PostFixAdmin. The
configuration file is in /etc/webapps/roundcubemail/config/config.inc.php which works
as an override over defaults.inc.php .
Apache configuration
If you are using Apache, copy the example configuration file to your webserver configuration
directory.
# cp /etc/webapps/roundcubemail/apache.conf /etc/httpd/conf/extra/httpd-roundcubemail.conf
/etc/httpd/conf/httpd.conf
Include conf/extra/httpd-roundcubemail.conf
To let users change their passwords from within Roundcube, do the following:
/etc/webapps/roundcubemail/config/config.inc.php
$config['plugins'] = array('password');
Configure the password plugin and make sure you alter the settings accordingly:
/usr/share/webapps/roundcubemail/plugins/password/config.inc.php
<?php
$config['password_driver'] = 'sql';
$config['password_db_dsn'] = 'mysql://<postfix_database_user>:<password>@localhost/<postfix_database_name>';
// If you are not using dovecot specify another algorithm explicitly e.g 'sha256-crypt'
$config['password_algorithm'] = 'dovecot';
// $config['password_algorithm_prefix'] = 'true';
// $config['password_dovecotpw_method'] = 'SHA512-CRYPT';
// $config['password_dovecotpw_with_method'] = true;
Fire it up
All necessary daemons should be started in order to test the configuration. Start both postfix
and dovecot .
Now for testing purposes, create a domain and mail account in PostfixAdmin. Try to login to this
account using Roundcube. Now send yourself a mail.
Testing
Now lets see if Postfix is going to deliver mail for our test user.
nc servername 25
helo testmail.org
mail from:<test@testmail.org>
rcpt to:<cactus@virtualdomain.tld>
data
quit
Error response
Maybe you have entered the wrong user/password for MySQL or the MySQL socket is not in the
right place.
This error will also occur if you neglect to run newaliases at least once before starting postfix.
MySQL is not required for local only usage of postfix.
550 5.1.1 <email@spam.me>: Recipient address rejected: User unknown in virtual mailbox table.
Double check content of mysql_virtual_mailboxes.cf and check the main.cf for mydestination
/home/vmailer/virtualdomain.tld/cactus@virtualdomain.tld
/home/vmailer/virtualdomain.tld/cactus@virtualdomain.tld/tmp
/home/vmailer/virtualdomain.tld/cactus@virtualdomain.tld/cur
/home/vmailer/virtualdomain.tld/cactus@virtualdomain.tld/new
/home/vmailer/virtualdomain.tld/cactus@virtualdomain.tld/new/1102974226.2704_0.bonk.testmail.org
The key is the last entry. This is an actual email, if you see that, it is working.
Optional Items
Although these items are not required, they definitely add more completeness to your setup
Quota
To enable mailbox quota support by dovecot, do the following:
dict {
quotadict = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
service dict {
unix_listener dict {
group = vmail
mode = 0660
user = vmail
user = root
}
service quota-warning {
user = vmail
unix_listener quota-warning {
group = vmail
mode = 0660
user = vmail
mail_plugins=quota
protocol pop3 {
mail_plugins = quota
pop3_uidl_format = %08Xu%08Xv
protocol lda {
mail_plugins = quota
postmaster_address = postmaster@yourdomain.com
protocol imap {
mail_plugin_dir = /usr/lib/dovecot/modules
plugin {
quota_rule2 = Trash:storage=+10%%
map {
pattern = priv/quota/storage
table = quota2
username_field = username
value_field = bytes
map {
pattern = priv/quota/messages
table = quota2
username_field = username
value_field = messages
#!/bin/sh
BOUNDARY="$1"
USER="$2"
MSG=""
MSG="Your mailbox is now overfull (>100%). In order for your account to continue functioning properly, you nee
d to remove some emails NOW."
MSG="Your mailbox is now over 95% full. Please remove some emails ASAP."
MSG="Your mailbox is now over 80% full. Please consider removing some emails to save space."
fi
From: postmaster@yourdomain.com
Dear User,
$MSG
Best regards,
EOF
user_query = SELECT '/home/vmail/%d/%n' as home, 'maildir:/home/vmail/%d/%n' as mail, 5000 AS uid, 5000 AS gid, c
oncat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
Set up LDA as described above under SpamAssassin. If you are not using SpamAssassin, the
pipe should look like this in /etc/postfix/master.cf :
virtual_transport = dovecot
You can set up quota per each mailbox in postfixadmin. Make sure the relevant lines in
config.inc.php look like this:
$CONF['quota'] = 'YES';
$CONF['quota_multiplier'] = '1024000';
Restart postfix and dovecot services. If things go well, you should be able to list all users' quota
and usage by the this command:
namespace inbox {
type = private
separator = /
prefix =
inbox = yes
namespace inbox {
mailbox Drafts {
auto = subscribe
special_use = \Drafts
mailbox Junk {
auto = subscribe
special_use = \Junk
mailbox Trash {
auto = subscribe
special_use = \Trash
mailbox Sent {
auto = subscribe
special_use = \Sent
In this section we enable IMAP namespace public folders combined with global and per-folder
ACLs.
### ACLs
mail_plugins = acl
protocol imap {
plugin {
acl = vfile
acl = vfile:/etc/dovecot/dovecot-acl
namespace {
type = public
separator = /
prefix = public/
location = maildir:/home/vmail/public:INDEXPVT=~/public
subscriptions = no
list = children
Create the root directory /home/vmail/public and the folders you want to publicly share, for
example (the period is required!) /home/vmail/public/.example-1 .
Finally, create and modify your global ACL file to allow users access to these folders:
/etc/dovecot/dovecot-acl
In the above example, user admin@example.com has access to, and can do anything to, all the
public folders. Edit to fit your specific needs.
Note:
lrwstipekxa are the permissions being granted. Visit the Dovecot wiki for further details.
Make sure the user subscribes to the folders in the client they are using.
Fighting Spam
Sidenotes
Troubleshooting
It may turn out that the Maildir /home/vmail/mail@domain.tld is just being created if there is
at least one email waiting. Otherwise there would not be any need for the directory creation
before.
$config['create_default_folders'] = true;
$config['protect_default_folders'] = true;
LMTP / Sieve
Is LMTP not connecting to sieve? Ensure that your server is not routing the messages locally. This
can be set in /etc/postfix/main.cf :
mydestination =
Are your emails sent to gmail users ending up in their junk/spam folders?
Google gmail (and most other large email providers) will send your emails straight into your
recipients junk / spam folder if you have not implemented SPF / DKIM / DMARC policies. (Hint:
Rspamd, via the link above, shows you how to set this up, and will DKIM sign your emails.)
See also
Gentoo:Complete Virtual Mail Server
Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.
Privacy policy
About ArchWiki
Disclaimers