Setup your own email server with Postfix on Ubuntu
Tags: ubuntu, tutorial, mail, postfix
Published: 2021-01-06 05:08:37

I was needed to send emails for my new project. I have explored SaaS services first. Free packages provided by them have a small amount of sending emails. So as long as I am a programmer, I decided to host my own sending email server.

After some research, I decided to stay with Postfix send-only configuration. Continue to read how to install and configure SMTP server to send emails and not send them to a spam folder.

### Prerequisites

I used a single-core VPS with Ubuntu 18.04 for just 2.5 USD per month. My main app uses a table in DB as an email queue, so I have two NodeJS apps: one to handle the queue and second to get delivery status and update it in the main app DB. Tell me if you want to see it. Here is only the Postfix part.

I also have a domain. Let it be **example.com** here. Since it will be used for a website, I am using a subdomain **email.example.com** for the email server.

### Postfix installation

Install Postfix using commands below:
```
sudo apt-get update
sudo apt install mailutils
```
During installation, select **Internet Site** option for type of mail configuration. As a **System mail name** enter your domain, e.g. example.com.  

You can see domain later with this command:  
```
cat /etc/mailname
```

### Server configuration

Open the main config file with an editor:
```
sudo nano /etc/postfix/main.cf
```
Find **inet_interfaces** parameter and change it to **loopback-only**. With that parameter, Postfix will not listen for any connection from outside of VPS.
```
inet_interfaces = loopback-only
```
Other parameters and values to change for now:
```
mydestination = $myhostname, localhost.$mydomain, localhost
```

Set your mail domain as a server hostname:
```
sudo hostnamectl set-hostname email.example.com
```
Check the hostname with the command:
```
hostname --f
```

Edit **/etc/hosts** file and add that subdomain with your remote IP:
```
1.2.3.4    email.example.com email
```
Command to restart Postfix after configuration change:
```
sudo systemctl restart postfix
```
Command to check current configuration:
```
postconf -d
```

### Create DKIM signature

Install DKIM tools:
```
sudo apt-get install opendkim opendkim-tools
```

Add the user to the group:
```
sudo gpasswd -a postfix opendkim
```

Open configuration file /etc/opendkim.conf:
```
sudo nano /etc/opendkim.conf
```
Then add or update parameters below in the configuration file:
```
Socket              inet:8892@localhost
Canonicalization    simple
Mode                sv
SubDomains          no
AutoRestart         yes
AutoRestartRate     10/1M
Background          yes
DNSTimeout          5
SignatureAlgorithm  rsa-sha256
UserID              opendkim
KeyTable            refile:/etc/opendkim/key.table
SigningTable        refile:/etc/opendkim/signing.table
ExternalIgnoreList  /etc/opendkim/trusted.hosts
InternalHosts       /etc/opendkim/trusted.hosts
```

Create a folder for DKIM keys:
```
sudo mkdir -p /etc/opendkim/keys
```

Change folder permissions:
```
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod go-rw /etc/opendkim/keys
```

Create opendkim signing table:
```
sudo nano /etc/opendkim/signing.table
```
With content inside:
```
*@example.com    default._domainkey.example.com
```
Then create key table: 
```
sudo nano /etc/opendkim/key.table
```
With content inside:
```
default._domainkey.example.com     example.com:default:/etc/opendkim/keys/example.com/default.private
```
Add trusted hosts:
```
sudo nano /etc/opendkim/trusted.hosts
```
With content inside:
```
127.0.0.1
localhost
*.example.com
```

Create keys folder:
```
sudo mkdir /etc/opendkim/keys/example.com
```
Then generate keys:
```bash
sudo opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s default -v
```
  
Change the private key file owner:
```bash
sudo chown opendkim:opendkim /etc/opendkim/keys/example.com/default.private
```
 
Then restart opendkim service:
```
sudo service opendkim restart
```

Check the key:
```
sudo opendkim-testkey -d example.com -s default -vvv
```

Now add or update parameters in the Postfix configuration file (/etc/postfix/main.cf):
```
milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8892
non_smtpd_milters = inet:localhost:8892
```

Then restart Postfix:
```
sudo systemctl restart postfix
```

### DNS configuration
You need to configure some DNS records. With that configuration, email services will not treat your emails as spam.

Add A record for the email.example.com:
```
Type: A
Name: email.example.com
Value: 1.2.3.4
```
Add SPF record:
```
Type: TXT
Name: example.com
Value: v=spf1 mx ~all
```
Add DMARC record:
```
Type: TXT
Name: _dmarc
Value: v=DMARC1; p=none
```
Add DKIM record. Use previously generated DKIM signature (/etc/opendkim/keys/example.com/default.txt):
```
Type: TXT
Name: default._domainkey
Value: v=DKIM1; h=sha256; k=rsa; p=you-key-here-without-spaces
```

Set PTR record (rDNS). You need to set it in your hosting provider's control panel.

Set email.example.com as a hostname and 4.3.2.1.in-addr.arpa as IP address. IP should be in reversed order.  

Check if it set correctly with the command below:
```
host 1.2.3.4
```

### Email encryption
Install Certbot:
```
sudo apt install certbot
```
Then generate keys:
```
sudo certbot certonly --standalone -d email.example.com
```
Configure Postfix to use that keys. Add or edit parameters below in /etc/postfix/main.cf:
```
smtpd_tls_cert_file = /etc/letsencrypt/live/email.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/email.example.com/privkey.pem
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/letsencrypt/live/email.example.com
smtp_tls_security_level=may
```
Then restart Postfix:
```bash
sudo service postfix restart
```

### Test

Send a test email with command:
```bash
echo "This is the body of the email" | mail -r test@example.com -s "This is the subject" your_email@gmail.com
```

Check spam score with online services, e.g.: https://www.mail-tester.com/ (not an ad).