Send email correctly from Ubuntu VPS

Today getting a VPS becomes more and more cheap. I believe every programmer has his/her own VPS. Sometime you want to run a blog on the VPS, thus you may need to send email such as reports from this VPS too.

To send email from the VPS correctly, you firstly need a clean IP. It requries:

(1) The IP has its own PTR record, for instance:

  # dig -x +short 

Here shows the IP has a valid PTR.

Without PTR you even can’t send email to most big players such as google.

In most cases you can setup IP’s PTR via the provider’s management panel.

(2) The IP has not been listed into any RBL blacklist.

RBL means realtime black lists, they are used for antispam purpose worldwide.

You must make sure your IP isn’t get blacklisted by any RBL.

Please validate it from the site:

(3) Your sending domain has the IP added into SPF record.

For example, the sending domain is:, which wants to send email from the IP, thus it should have this SPF record setup in DNS: 300 IN TXT "v=spf1  ip4: ~all" 

Without the SPF setting most recipients may reject your messages, because they don’t know if the domain has the permission to use this IP for sending.

After you have finished setup the IP, you just enable MTA relay on localhost, and send email from your scripts to this relay. MTA relay will route your messages out from localhost to other recipients.

The important step is to make sure you have correct hostname to the VPS, and the hostname should be resolvable, either via DNS or hosts file.

Then, install postfix as relay on localhost:

# apt install postfix
# vi /etc/postfix/

The last two lines should be changed to:

 inet_interfaces =
 inet_protocols = ipv4 

The first line make sure postfix run on localhost only for security reasons. The second line let postfix send mail only via ipv4 protocol, using ipv6 protocol google’s mail servers may reject to talk to us.

Finally restart postfix:

# /etc/init.d/postfix restart

The MTA relay should be working now.

How to send email from within a script? It has many methods. I can show you the perl way below.

We want to install cpanm and the needed modules.

# apt install cpanminus
# cpanm Email::Send::YYClouds
# cpanm LWP::Simple
# cpanm JSON

Then write a script to send statistics report for blog visit.

 use strict;
 use POSIX;
 use Email::Send::YYClouds;
 use LWP::Simple;
 use JSON;
 use utf8;
 my $date = strftime "%F",localtime;
 my $subject = "Daily visit statistics - $date";
 my $log = "/var/log/apache2/access.log";
 my %hash;
 open HD,$log or die $!;
 while (<HD>) {
     # a regex for matching the correct visit IP
     next unless m{^(\d+\.\d+\.\d+\.\d+).*\"GET /.*?/ HTTP/};
     my $ip=$1; 
     $hash{$ip} ++;
 close HD;
 my $body = '<table style="width:100%">' . "\n";
 for my $ip (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
     my $num = $hash{$ip};
     my $loc ="";
     if ($num>=2) {
         # get IP location via public api
         $loc = ip_loc($ip);
     $body .= <<DATA;
 $body .= '</table>';
 # send email
 my $msg = Email::Send::YYClouds->new();
 $msg->send(recepient => ['[email protected]'],
            sender => '[email protected]',
            smtprelay => 'localhost',
            subject => $subject,
            body => $body,

 # ip query
 sub ip_loc {
     my $ip = shift;
     my $content = get("$ip/json/");
     if (defined $content) {
         my $json = decode_json($content);
         return $json->{city}. ", " . $json->{region}. ", ". $json->{country};
     } else {
         return "";

After you get message, the content looks similar to this table:, Virginia, US, Virginia, US, Virginia, US, Washington, US, Guangdong, CN, Virginia, US, Virginia, US am Main, Hesse, DE, Washington, US, Bursa, TR, Virginia, US, Guangdong, CN, Île-de-France, FR, Washington, US Chung, Tsuen Wan District, HK, Rivne, UA, Quebec, CA, Virginia, US

Until now, all is done well.