Email me maybe

Here is the full command history, plus a few, that I used to create the email server live in front of a real live audience.

Feel free to use it to create an email server yourself, but please notice the following caveats:

  • Replace the domain with yours. I could have used variable substitution and replace it with ${DOMAIN} throughout the script, but the intent here is for you to understand the steps, and ultimately I felt that it impaired understandability.
  • Replace the user if you so wish
  • Don't run this as a script, but copy the commands one by one and try to understand what they do meanwhile.

If you're in for a quick solution there are already a ton of them online, namely Mail-in-a-Box that have a lot more bells and whistles, including webmail and advanced spam and virus protection through spamassassin and ClamAV.

So although this makes up for a pretty sturdy single usage email server, use this script to learn, not necessarily to deploy a production grade service.



Configuring postfix

Install postfix, our mail transfer agent. When running the configuration screen choose 'Internet Site' as your installation type, and then type your domain name in the following dialogue, in our case ''.

apt install postfix

Install mailutils and mutt, two small MUAs that we can use to check our email on the server itself, after we connect to it via SSH.

apt install mailutils mutt

Install dovecot, our MDA that will provide IMAP4 and POP3 so we can check our email on our cell phones and laptops, and will provide authentication to our MTA so spammers don't abuse our server.

apt install dovecot-common dovecot-imapd dovecot-pop3d

Tell postfix about the domain it is managing.

postconf -e "mydomain ="

Tell postfix where to store messages, in our case we'll store the messages as files on each user's home directory, in a directory called "Maildir". If you change this remember to change it as well in later parts of the guide, namely dovecot's mail location and mutt's.

postconf -e 'home_mailbox = Maildir/'

Install LetsEncrypt to provide us with certificates so that we can make communications coming in the server secure. This will make people that connect to the server to deliver us email more at ease by knowing that they're connecting to the right place, and make us less prone to hacking attempts.

Notice that we're passing the hostname (the name of the computer where our server is running) and not our email domain here. This is important.

apt install certbot
certbot certonly --standalone -d

Tell postfix where the generated certificates are. Remember that this may change according to your own configuration and domain. See the previous certbot command output to understand where those files were stored.

postconf -e "smtpd_tls_cert_file = /etc/letsencrypt/live/"
postconf -e "smtpd_tls_key_file = /etc/letsencrypt/live/"

Tell postfix to use TLS (secure communications).

postconf -e 'smtp_tls_security_level = may'
postconf -e 'smtpd_tls_security_level = may'
postconf -e 'smtp_tls_note_starttls_offer = yes'
postconf -e 'smtpd_tls_loglevel = 1'
postconf -e 'smtpd_tls_received_header = yes'

Tell postfix to use dovecot to authenticate users trying to send email through our server. This is important to prevent spammers and unauthorized users from abusing our server and others.

Don't leave your server out in the open! It will make big providers like GMail block you, making you unable to send them any email, and might even leave you liable to prosecution.

postconf -e 'smtpd_sasl_type = dovecot'
postconf -e 'smtpd_sasl_path = private/auth'
postconf -e 'smtpd_sasl_local_domain ='
postconf -e 'smtpd_sasl_security_options = noanonymous'
postconf -e 'broken_sasl_auth_clients = yes'
postconf -e 'smtpd_sasl_auth_enable = yes'
postconf -e 'smtpd_recipient_restrictions =permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination'

Tell postfix you want virtual aliases. Virtual aliases are email aliases. Let's suppose you have one user, in our case pedro but want that user to receive email from several addresses like, or All you need is to set up an alias pointing those addresses to pedro's mail box.

postconf -e 'virtual_alias_domains = $mydomain'
postconf -e 'virtual_alias_maps = hash:/etc/postfix/virtual'

Edit the file where the virtual aliases will be stored. The following command just replaces the contents of the file /etc/postfix/virtual with the lines up to EOF.

cat << EOF > /etc/postfix/virtual root root pedro

After updating the virtual alias file you must run postmap on it, as postfix needs this file to be in a specific format and won't read it itself until you tun this command.

postmap /etc/postfix/virtual

Finally restart postfix, and you should be done with it for now.

systemctl restart postfix

Creating mail boxes

Create a mailbox structure for users that will be created in the future. This will create a Drafts, Sent, Trash and templates folder in every mail box in users created from here on. If you already have users this won't apply to them.

maildirmake.dovecot /etc/skel/Maildir
maildirmake.dovecot /etc/skel/Maildir/.Drafts
maildirmake.dovecot /etc/skel/Maildir/.Sent
maildirmake.dovecot /etc/skel/Maildir/.Trash
maildirmake.dovecot /etc/skel/Maildir/.Templates

echo 'export MAIL=~/Maildir' | sudo tee -a /etc/bash.bashrc | sudo tee -a /etc/profile.d/

Create a user to receive our mail and their respective mail box.

useradd -m -s /bin/bash pedro

cp -r /etc/skel/Maildir /home/pedro/
chown -R pedro:pedro /home/pedro/Maildir
chmod -R 700 /home/pedro/Maildir
adduser pedro mail

Set a password for the user we created. Remember this password, it will be the one you'll use when configuring your phone to receive email, for example.

passwd pedro

Configuring dovecot

Tell dovecot that we don't want to enable authentication in plaintext, only through a more secure mechanism. This is more of a technicality but makes you more secure. Also tell it that it may use our login user/password to perform that authentication. That's how the password we set for our user gets in the system.

vim /etc/dovecot/conf.d/10-auth.conf

#/ Find and change the following lines:
#/ disable_plaintext_auth = yes
#/ auth_mechanisms = plain login

Tell dovecot where messages are stored. If you changed this in postfix you'll also have to change it here. If you forget you'll still receive and send email in the server without problems, but won't be able to access it from the outside in your phone or computer via IMAP4 because dovecot will be looking for it in the wrong place.

vim /etc/dovecot/conf.d/10-mail.conf

#/ Find and change the following lines:
#/ mail_location = maildir:~/Maildir

Tell dovecot which services we need, which will only be IMAP4 for us to access our messages without having to SSH into the server, and auth to help postfix with the authentication we set up earlier.

vim /etc/dovecot/conf.d/10-master.conf

#/ Find and change the following lines:
#/ service imap-login {
#/    inet_listener imap {
#/       port = 143
#/    }
#/ }
#/ service auth {
#/    unix_listener /var/spool/postfix/private/auth {
#/       mode = 0660
#/       user = postfix
#/       group = postfix
#/    }
#/ }

Enable TLS, tell where certificates are and tell which protocols to avoid due to them being insecure. Again, more of a technicality but it makes you more secure.

vim /etc/dovecot/conf.d/10-ssl.conf

#/ Find and change the following lines:
#/ ssl = required
#/ ssl_cert = </etc/letsencrypt/live/
#/ ssl_key = </etc/letsencrypt/live/
#/ ssl_protocols = !SSLv2 !SSLv3

Restart dovecot and everything's in place.

dovecot -n
systemctl restart dovecot

Configure OpenDKIM

Install OpenDKIM

apt install opendkim opendkim-tools

Generate your private and public key. Keep your private key safe!

mkdir -p /etc/opendkim
cd /etc/opendkim

opendkim-genkey -b 2048 -d -s

Configure opendkim.

cat << EOF > /etc/opendkim.conf
Syslog yes

Selector mail
Mode sv
SubDomains yes
AutoRestart yes
Background yes
Canonicalization relaxed/relaxed
DNSTimeout 5
SignatureAlgorithm rsa-sha256
X-Header yes
Logwhy yes

InternalHosts /etc/opendkim/internalhosts
KeyTable /etc/opendkim/keytable
SigningTable refile:/etc/opendkim/signtable

OversignHeaders From

Tell opendkim which key signs which selector.

cat << EOF > /etc/opendkim/keytable

Tell opendkim which selector corresponds to which domain.

cat << EOF > /etc/opendkim/signtable

Tell opendkim where our email is coming from so it knows whether to sign it or to check its signature. Remember that when you're connecting from your phone it counts as a local send, so you only need to put your own mail servers' hostnames here, in this case just one.

cat << EOF > /etc/opendkim/internalhosts

Make opendkim listen on a local port so that postfix can connect to it to check and sign messages.

cat <<EOF >> /etc/default/opendkim

Set the correct permissions on OpenDKIM files

chown opendkim:opendkim /etc/opendkim -R

Tell postfix to sign messages going out, and to check messages coming in.

postconf -e 'smtpd_milters = inet:localhost:8891'
postconf -e 'non_smtpd_milters = inet:localhost:8891'
postconf -e 'milter_default_action = accept'
postconf -e 'milter_protocol = 2'

Check the file that opendkim set up for you with the record you must include in your dns under Use only what's inside the parenthesis.

cat /etc/opendkim/

Everything's ready, restart everything.

systemctl restart opendkim
systemctl restart postfix

Bonus round

Some extra stuff that makes your setup slightly better.

Tell postfix to restrict weird and non existing hostnames from sending mail. This is a spam protection.

postconf -e 'smtpd_helo_required = yes'
postconf -e 'smtpd_helo_restrictions = reject_non_fqdn_helo_hostname,reject_invalid_helo_hostname,reject_unknown_helo_hostname'

Tell postfix to disable the VRFY command, which is a command in the SMTP protocol that you may use to check if an account exists on a server. We don't want to expose our accounts.

postconf -e 'disable_vrfy_command = yes'

Give postfix a handful of restrictions to connections that come in to drop email. The last two are particularly interesting as they will make your server check if the sender is present in any blacklist thus eliminating a great deal of spam hosts.

postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,\

Tell postfix to delay rejections to log more information about perpetrators. This will make your server spend a little more effort on spam messages but you'll be able to glimpse at them in your logs.

postconf -e 'smtpd_delay_reject = yes'

Configuring mutt

Mutt is a great client that you can use in the terminal if you choose to connect to your server via SSH. You may also use it on your local terminal if you wish, but that's up to you.

When using it remotely via SSH, mutt doesn't need dovecot or IMAP4 simply because as it is already running on the same server where your messages are stored it can directly access to them (remember they are stored as files).

cat <<EOF > ~/.muttrc
set mbox_type=Maildir
set folder="~/Maildir"
set mask="!^\\.[^.]"
set mbox="~/Maildir"
set record="+.Sent"
set postponed="+.Drafts"
set spoolfile="~/Maildir"

Now if you want to check your mail upon login you just have to write mutt.



Remmeber to:

  • Configure your SPF, DKIM and DMARC records on your DNS!
  • Backup your mailbox regularly, it is stored in ~/Mailbox.