Skip to main content

How to set up a mail server on a GNU / Linux system

Step by step set up a mail server

Creating a Mail Server on Ubuntu (Postfix, Courier, SSL/TLS,SpamAssassin, ClamAV, Amavis)

Easy to follow howto on setting up a mail server with unlimited users and domains

with IMAP/Pop access, anti-spam, anti-virus, secure authentication, encrypted traffic, web mail interface and more.
Based on an Ubuntu distribution platform, but instructions are distro generic.
Examples are run on Amazon AWS ec2, but only for demonstration purposes.
We’ll be building a mail server made up of the following components:
  • Postfix is the mail transfer agent (MTA) responsible for accepting new messages and storing them on your server
    as well as allowing authorised users to send e-mail.
  • Courier sits in front of Postfix and provides an IMAP and POP3 interface for clients to connect to.
  • SASL with SSL and TLS allows you to authenticate and communicate with the mail server securely.
  • SpamAssassin will analyse your e-mails as they arrive and will filter out what it thinks is spam.
  • ClamAV will scan e-mails for viruses before delivering it to your inbox.
  • Amavis ties SpamAssasin and ClamAV together, and is itself hooked into Postfix.
  • MySQL will be used to manage user accounts and e-mail forwarding.
First, switch to the root user unless, of course, you like typing sudo
sudo su -
For simplicity, we’ll install all the software in one go:
apt-get update
apt-get install -y mysql-server postfix postfix-mysql libsasl2-modules libsasl2-modules-sql 
libgsasl7 libauthen-sasl-cyrus-perl sasl2-bin libpam-mysql clamav-base libclamav6 clamav-daemon 
clamav-freshclam amavisd-new spamassassin spamc courier-base courier-authdaemon courier-authlib-mysql 
courier-imap courier-imap-ssl courier-pop courier-pop-ssl courier-ssl
During the installation of MySQL you will be prompted for the root user password, as shown:
MySQL password prompt
Enter a secure password, and don’t forget it!
Similarly, during the installation of Courier you will be presented with the following configuration prompts:
Courier configuration wizard
Choose No
Courier configuration wizard
Choose OK
Courier configuration wizard
Choose Internet Site
Courier configuration wizard
Enter your mail server name (e.g. replace with your own domain).
Make sure you have this subdomain configured in your DNS records.
Courier configuration wizard
Choose OK
I won’t walk you through the parameters we’re using when configuring Postfix as I want to keep this guide light.
If you’re interested, you can find more information from the main pages.
mv /etc/postfix/{,.default}
vi /etc/postfix/
Copy/paste the following (change all instances of
myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
readme_directory = no
mydestination =
relayhost =
mynetworks = [::ffff:]/104 [::1]/128
mynetworks_style = host
mailbox_size_limit = 0
virtual_mailbox_limit = 0
recipient_delimiter = +
inet_interfaces = all
message_size_limit = 0

# SMTP Authentication (SASL)

smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =

# Encrypted transfer (SSL/TLS)

smtp_use_tls = yes
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/ssl/private/
smtpd_tls_key_file = /etc/ssl/private/
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# Basic SPAM prevention

smtpd_helo_required = yes
smtpd_delay_reject = yes
disable_vrfy_command = yes
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, reject
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject

# Force incoming mail to go through Amavis

content_filter = amavis:[]:10024
receive_override_options = no_address_mappings

# Virtual user mappings

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
virtual_mailbox_base = /var/spool/mail/virtual
virtual_mailbox_maps = mysql:/etc/postfix/maps/
virtual_uid_maps = static:5000
virtual_gid_maps =  static:5000
virtual_alias_maps = mysql:/etc/postfix/maps/
virtual_mailbox_domains = mysql:/etc/postfix/maps/
mv /etc/postfix/{,.default}
vi /etc/postfix/
Copy/paste the following (no changes required):
# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
# Do not forget to execute "postfix reload" after editing this file.
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
submission inet n       -       -       -       -       smtpd
pickup    fifo  n       -       -       60      1       pickup
  -o content_filter=
  -o receive_override_options=no_header_body_checks
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       -       1000?   1       tlsmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       -       -       -       smtp
# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
relay     unix  -       -       -       -       -       smtp
 -o smtp_fallback_relay=
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
retry     unix  -       -       -       -       -       error
discard   unix  -       -       -       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       -       -       -       lmtp
anvil     unix  -       -       -       -       1       anvil
scache    unix  -       -       -       -       1       scache
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
# Many of the following services use the Postfix pipe(8) delivery
# agent.  See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in maildrop_destination_recipient_limit=1
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# See the Postfix UUCP_README file for configuration details.
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# Other external delivery methods.
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FR user=list argv=/usr/lib/mailman/bin/
  ${nexthop} ${user}
amavis    unix -        -       -       -       2       smtp
  -o smtp_data_done_timeout=1200
  -o smtp_send_xforward_command=yes
  -o disable_dns_lookups=yes
  -o max_use=20 inet n  -       -       -       -       smtpd
  -o content_filter=
  -o local_recipient_maps=
  -o relay_recipient_maps=
  -o smtpd_restriction_classes=
  -o smtpd_delay_reject=no
  -o smtpd_client_restrictions=permit_mynetworks,reject
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o smtpd_data_restrictions=reject_unauth_pipelining
  -o smtpd_end_of_data_restrictions=
  -o mynetworks=
  -o smtpd_error_sleep_time=0
  -o smtpd_soft_error_limit=1001
  -o smtpd_hard_error_limit=1000
  -o smtpd_client_connection_count_limit=0
  -o smtpd_client_connection_rate_limit=0
  -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
As all our mail users are going to be virtual (i.e. we’re not going to create physical user accounts for each user),
we only need to create one mail directory and one user account.
groupadd virtual -g 5000
useradd -r -g "virtual" -G "users" -c "Virtual User" -u 5000 virtual
mkdir /var/spool/mail/virtual
chown virtual:virtual /var/spool/mail/virtual
Make sure that, if the UID or GID differs from 5000, you update the virtual_uid_maps andvirtual_gid_maps values in /etc/postfix/, and MYSQL_UID_FIELD andMYSQL_GID_FIELD in /etc/courier/authmysqlrc (later in this guide).
Now we’ll create the database which will store the mail user configuration and forwarding rules.
mysql -uroot -p
Enter the password you created during the MySQL installation.
Copy/paste the following (change and change the admin’s password to something more secure):
GRANT ALL ON mail.* TO mail@localhost IDENTIFIED BY 'mailuserpassword';

USE mail;

  `source` varchar(255) NOT NULL,
  `destination` varchar(255) NOT NULL default '',
  `enabled` tinyint(1) unsigned NOT NULL default '1',
  PRIMARY KEY  (`source`)

  `domain` varchar(255) NOT NULL default '',
  `transport` varchar(255) NOT NULL default 'virtual:',
  `enabled` tinyint(1) unsigned NOT NULL default '1',
  PRIMARY KEY  (`domain`)

  `email` varchar(255) NOT NULL default '',
  `password` varchar(255) NOT NULL default '',
  `name` varchar(255) default '',
  `quota` varchar(255) default NULL,
  `enabled` tinyint(1) unsigned NOT NULL default '1',
  PRIMARY KEY  (`email`)

INSERT INTO `alias` (`source`, `destination`, `enabled`) VALUES ('@localhost', '', 1);
INSERT INTO `alias` (`source`, `destination`, `enabled`) VALUES ('@localhost.localdomain', '@localhost', 1);
INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('localhost', 'virtual:', 1);
INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('localhost.localdomain', 'virtual:', 1);
INSERT INTO `domain` (`domain`, `transport`, `enabled`) VALUES ('', 'virtual:', 1);
INSERT INTO `user` (`email`, `password`, `name`, `quota`, `enabled`)
VALUES ('', ENCRYPT('changeme'), 'Administrator', NULL, 1);
Note that we’re encrypting the password. Some guides will recommend storing the password in clear text so that you can configure Postfix to support CRAM-* (e.g. CRAM-MD5) authentication methods. I think it’s much more secure to store these passwords encrypted and use SSL/TLS to encrypt your authentication requests. For that reason, we don’t need to store clear text passwords and we don’t need to provide CRAM-* support.
Now that the database is in place we can create the map files to tell Postfix how to communicate with it.
mkdir /etc/postfix/maps
vi /etc/postfix/maps/
Copy/paste the following (change mailuserpassword):
additional_conditions=and enabled = 1
vi /etc/postfix/maps/
Copy/paste the following (change mailuserpassword):
user = mail
password = mailuserpassword
dbname = mail
table = domain
select_field = domain
where_field = domain
hosts =
additional_conditions = and enabled = 1
vi /etc/postfix/maps/
Copy/paste the following (change mailuserpassword):
user = mail
password = mailuserpassword
dbname = mail
table = user
select_field = CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/')
where_field = email
hosts =
additional_conditions = and enabled = 1
Set restrictive read permissions as these files contain the MySQL mail user’s password.
chmod 700 /etc/postfix/maps/*
chown postfix:postfix /etc/postfix/maps/*
The final part of configuring Postfix is to configure the authentication mechanism. SASL is a authentication layer that provides the ability to receive a user’s credentials in a variety of formats.
mkdir -p /var/spool/postfix/var/run/saslauthd
mkdir /etc/postfix/sasl
adduser postfix sasl
vi /etc/postfix/sasl/smtpd.conf
Copy/paste the following (change mailuserpassword):
pwcheck_method: saslauthd
auxprop_plugin: sql
mech_list: plain login
sql_engine: mysql
sql_user: mail
sql_passwd: mailuserpassword
sql_database: mail
sql_select: SELECT password FROM user WHERE email='%u@%r' AND enabled = 1
chmod -R 700 /etc/postfix/sasl/smtpd.conf
mv /etc/default/saslauthd{,.default}
vi /etc/default/saslauthd
Copy/paste the following (no changes required):
DESC="SASL Authentication Daemon"
OPTIONS="-r -c -m /var/spool/postfix/var/run/saslauthd"
vi /etc/pam.d/smtp
Copy/paste the following (change all instances of mailuserpassword):
auth    required user=mail passwd=mailuserpassword host=
db=mail table=user usercolumn=email passwdcolumn=password crypt=1
account sufficient user=mail passwd=mailuserpassword host=
db=mail table=user usercolumn=email passwdcolumn=password crypt=1
chmod 700 /etc/pam.d/smtp
Now let’s configure Courier.
with part 2


Popular posts from this blog

Asterisk – CLI commands

Agent commands agent logoff  - Sets an agent offline agent show  - Show status of agents agent show online  - Show all online agents AGI commands agi dump html  - Dumps a list of AGI commands in HTML format agi exec  - Add AGI command to a channel in Async AGI agi set debug [on|off]  - Enable/Disable AGI debugging agi show commands [topic]  - List AGI commands or specific help dnsmgr refresh  - Performs an immediate refresh dnsmgr reload  - Reloads the DNS manager configuration dnsmgr status  - Display the DNS manager status Calendar commands calendar dump sched  - Dump calendar sched context calendar show calendar  - Display information about a calendar calendar show calendars  - Show registered calendars Channel commands channel originate  - Originate a call channel redirect  - Redirect a call channel request hangup  - Request a hangup on a given channel Cli commands cli check permissions  - Try a permissions config for a user cli reload permi

Making video calls – Asterisk tutorial

sip.conf To be able to send video during a call, codec h263 and video support must be enabled. This is done by adding three lines in the sip.conf file (Location: /etc/asterisk/sip.conf). Add the following lines in the [general] tab of the file. videosupport = yes ; Enable video allow = h263 ; H.263 is our video codec allow = h263p ; H.263p is the enhanced video codec Reload Reload the sip.conf file by running the following command in the CLI console:  reload Config in Softphone  Click “Softphone”  Click “Preferences”    Click “Video Codecs”  Verify that h263 and h263+ are selected Click “OK”  Click the video (Webcam) icon to display the video  Dial the other sip phone (number 1002)  Click “Show Video” on the two sip phones.

Step by step: Configure call recording - Asterisk tutorials

This article will cover enabling asterisk to record calls. You may want this to interview people over the phone, podcast, or some other purpose. In features.conf, under: [featuremap] uncomment the line that looks like this: automixmon => *3 ; One Touch Record a.k.a. Touch MixMonitor — Make sure to set the X and/or x option in the Dial() or    Queue() app call! Then, enable the X option for Dial() in your dialplan in extensions.conf: PLEASE NOTE:  change you need to make – add the X  your dial rule make look different with me. exten => s,n,Dial(SIP/100,60) make it this instead: exten => s,n,Dial(SIP/100,60,X) The X is what tells Asterisk to allow callers to dial *3 during a call to enable or disable recording. From the asterisk console (run asterisk -r), you should see a line like this appear when the user starts a recording: – User hit ‘*3′ to record call. filename: auto-xxxxx-EXTENSION-DIALEDNUMBER When the recording ends, un