VPS Benchmarks: Amazon EC2 and Lightsail, Azure, DigitalOcean, Google, Hostworld, Linode, OVH, UpCloud, VPSServer, VPS.net, Vultr

I recently needed to have a look at moving some services to a different VPS provider for redundancy so I decided to benchmark my options to compare them.

The plan selected was whichever had 16GB of RAM (though Google is 15GB). The selected datacenter was always London (Azure only says UK South). The fastest storage options were selected. The OS was CentOS 7 since that was mostly supported (Amazon and VPS.net do not support CentOS 8).

For Amazon and DigitalOcean I tested 2 options; for Amazon I was curious as to how Lightsail stacked up against vanilla EC2 and I also wondered if the 50% hike in price for DigitalOcean General Purpose was worth it versus the Standard VPSs from them that I’d used in the past.

The test were generally run a few times and averaged out. I used the latest sysbench to measure CPU speed, File I/O and MySQL transaction speed for comparison.

ProviderPlanvCPU coresRAM (GB)CPU Events/sRead MiB/sWrite MiB/sMySQL tran/s
AmazonEC2 t3a.xlarge416514.7918.8412.561099.59
AmazonLightsail 16GB416337.4415.5510.371603.31
AzureB4MS416358.033.572.38448.60
DigitalOceanGeneral Purpose 16GB416434.4620.8513.901328.43
DigitalOceanStandard 16GB616304.3710.797.191078.46
Google Cloud Computen1-standard-4415345.2311.897.93976.040
HostworldVPS-6816541.2721.6014.401428.03
KamateraCustom (Availability)416413.7536.9624.642634.97
LinodeShared 16GB616501.9143.2128.801728.78
OVH CloudElite816267.6731.0020.671729.30
UK2SSD VPS V6-20416254.3323.3715.581317.89
UpCloud16GB616449.8951.1034.042204.81
VPS.netV6-19416248.313.912.61630.87
VPSServerStandard 16GB816156.2316.8211.21844.11
VultrCloud Compute 16GB616403.3735.2023.461691.64

For disk I/O UpCloud is the clear winner and Azure and VPS.net are VERY distant losers – and this is with Azure’s Premium SSD option. Hostworld just wins the CPU crown, but its I/O is less impressive. Of the rest I’d say Linode is a pretty solid performer. There are some distinctly average performances from some of these. I was quite surprised by some of the numbers and ran them a few extra times to make sure (VPSServer’s terrible CPU speed and Azure and VPS.net’s shocking I/O for example).

YMMV.

Price-wise, the top 4 in terms of speed have very similar pricing (that’s UpCloud, Linode, Kamatera and Vultr in order of read speed), then OVH and Hostworld are both significantly cheaper. Hostworld is actually the cheapest in the list with the fastest CPU but only mid-range disk I/O.

The commands used to benchmark were as follows:

For CPU Events per second:
sysbench cpu --cpu-max-prime=20000 run

For read and write performance in MiB/s (after creating 150GB of files):
sysbench fileio --file-total-size=150G --file-test-mode=rndrw --time=300 --max-requests=0 run

For MySQL transactions per second (after creating a test database with 1 million rows):
sysbench oltp_read_write --table-size=1000000 --db-driver=mysql --mysql-db=test --time=60 --max-requests=0 --threads=8 run

Install OSSIM/OSSEC agent (CentOS 7)

I couldn’t find proper instructions on doing this anywhere so here are mine:

In your OSSIM portal go to Environment -> Assets & Groups -> Add Assets and enter the name/IP of the asset you want to add.

On the agent run:

yum -y install libevent-devel pcre2-devel openssl-devel
wget https://github.com/ossec/ossec-hids/archive/3.6.0.tar.gz
tar xzf 3.6.0.tar.gz
cd ossec-hids-3.6.0/
./install.sh

…select the defaults and type in agent when prompted.

Back in OSSIM go to Environment -> Detection -> Agents -> Add Agent

Find the new asset in the dropdown and click ADD. Next to the new agent there is a picture of a key, click that and copy the long key it shows you.

On the agent run:

/var/ossec/bin/manage_agents

Choose I to import a key, then paste the key then Q to quit.

Check the file /var/ossec/etc/ossec.conf to make sure it’s monitoring the necessary logs. Then to start the agent run:

/var/ossec/bin/ossec-control start

It can take 10-15 minutes for OSSIM to show the agent as active.

VPS video encoding speed comparison

I was looking at VPS providers for batch video encoding runs. These numbers are in no way a comprehensive test, I was just surprised by some of them so I thought I’d share.

My source file is a 04:17 1080p mp4 and I ran it through HandbrakeCLI to create an optimised 720p version – I used the exact same commands on all the servers I span up. This is how long it took to encode:

LINODE
$80 6 cores 2:21
$160 8 cores 1:53
$120 8 dedicated cores 2:53 (slower than 6 non-dedicated cores which is half the price)

DIGITAL OCEAN
$160 8 cores 2:35
$160 8 optimised cores 1:30

VULTR
$160 8 cores 1:47

I was looking to compare the $160/8 core price point, the Linode $80 test is only included to show that it is faster that the 8 dedicated cores option.

CentOS 7 Apache with PHP 7.1 and FCGId

Right, installing PHP 7.1 is simple, just get the Webtatic repo and install it

# rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
# yum install -y mod_php71w php71w-cli php71w-common php71w-gd php71w-mbstring php71w-mcrypt php71w-mysqlnd php71w-xml php71w-opcache

If you want to use FCGId instead of mod_php install and configure FPM and mod_fcgid (make sure you remove mod_php71w from the previous command!)

# yum install -y php71w-fpm mod_fcgid
# chkconfig --levels 235 php-fpm on
# vi /etc/php-fpm.d/www.conf

and change the following

listen = 127.0.0.1:9000
to
listen = /tmp/php5-fpm.sock

then we need to create some configuration and a wrapper script:

# vi /etc/httpd/conf.d/fcgid.conf

and add this to the end

<Directory /var/www/html/>
AddHandler fcgid-script .php
FCGIWrapper /etc/httpd/conf.d/wrapper .php
Options ExecCGI Indexes FollowSymLinks MultiViews
AllowOverride None
Require all granted
</Directory>

you can change the webroot directory to where you server your pages from if it’s different. Then create the wrapper:

# vi /etc/httpd/conf.d/wrapper

and add the following

#!/bin/sh
PHPRC=/etc/
export PHPRC
export PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_CHILDREN=8
exec /usr/bin/php-cgi

Nearly there! Final step

# vi /etc/httpd/conf/httpd.conf

and look for the DirectoryIndex variable, then add your php index files

DirectoryIndex index.php index.html index.htm

Then update the permissions and fire it all up

# chmod 777 /etc/httpd/conf.d/wrapper
# systemctl start php-fpm.service
# systemctl restart httpd.service

Done.

PHPMailer NTLM (MS Exchange) SMTP authentication

PHPmailer does not work with NTLM authentication and insists on using mhash() which is deprecated – so you need to edit the file in /extras called ntlm_sasl_client.php

Find the code that checks if mhash() is installed and replace the 3 mhashes with hash instead:

|| !function_exists($function = "mhash")
) {
$extensions = array(
"mcrypt_encrypt" => "mcrypt",
"mhash" => "mhash"

Then replace these three lines:

$unicode = $this->ASCIIToUnicode($password);
$md4 = mhash(MHASH_MD4, $unicode);
$padded = $md4 . str_repeat(chr(0), 21 - strlen($md4));

With the following:

$unicode = iconv('UTF-8', 'UTF-16LE', $password);
$padded = pack('H*', hash('md4', $unicode));

And the library now works. When sending you will need to define a couple of extra bits:

$mail->SMTPSecure = 'ntlm';
$mail->Realm = "yourdomain.com";
$mail->Workstation = "WORKSTATION1";

…obviously replacing the domain and workstation name with your own values.

Postfix ban failed logins script

Fail2ban hasn’t been working for me, I still have people running brute force attacks on my Postfix server, so I though I’d rig up something myself.

This consists of a bash script that identifies multiple failures and bans them, run on cron every 10 minutes. It checks for both smtp and pop/imap login failures.

#!/bin/sh
# postfix ban failed login ips
# get all failed ip addresses into files
cat /var/log/maillog | grep "authentication failed" | grep -Eo "([0-9]{1,3}[\.]){3}[0-9]{1,3}" > ~admin/mail_fail_smtp
cat /var/log/maillog | grep "auth failed" | grep -Eo "rip=([0-9]{1,3}[\.]){3}[0-9]{1,3}" > ~admin/mail_fail_imap
find ~admin/mail_fail_imap -type f -exec sed -i 's/rip=//g' {} \;
# only get over 5 fails (change the limit= part to change)
sort ~admin/mail_fail_imap | uniq -cd | awk -v limit=5 '$1 > limit{print $2}' > ~admin/mail_fail_imap_over5
sort ~admin/mail_fail_smtp | uniq -cd | awk -v limit=5 '$1 > limit{print $2}' > ~admin/mail_fail_smtp_over5
# read through files and add IP to hosts.deny if not there already
while read p; do
if grep $p /etc/hosts.deny; then
echo $p " already added"
else
echo ALL: $p >> /etc/hosts.deny
fi
done < ~admin/mail_fail_smtp_over5
while read p; do
if grep $p /etc/hosts.deny; then
echo $p " already added"
else
echo ALL: $p >> /etc/hosts.deny
fi
done < ~admin/mail_fail_imap_over5
# clean up
rm -f ~admin/mail_fail_smtp
rm -f ~admin/mail_fail_imap
rm -f ~admin/mail_fail_smtp_over5
rm -f ~admin/mail_fail_imap_over5

Then added to crontab:

*/10 * * * * /home/admin/postfix_ban_ips.sh > /dev/null

And just in case the localhost fails and is unintentionally blocked (this is quicker than filtering it out above):

echo "ALL: 127.0.0.1" >> /etc/hosts.allow

Convert HTML table to CSV

Just a quick one – I needed a script to convert a table to a csv, so this is what I came up with. See the annotations for notes:

//html table is in variable $report
$report = str_replace(array("\n", "\r"), "", $report); //remove existing line breaks
$report = str_replace('"', '\"', $report); //escape existing quote marks
$csv_lines = explode('</tr>', $report); //explode by end of table row
$csv_report = ''; //define output
  foreach ($csv_lines as $this_line) { //each row
  $csv_cells = explode('</td>', $this_line); //explode by end of table cell
  $csv_newcells = array(); //define new cells
    foreach ($csv_cells as $this_cell) { //each cell
    $this_cell = strip_tags($this_cell); //remove any html tags
    $this_cell = html_entity_decode($this_cell); //remove any html characters
    $this_cell = trim($this_cell); //trim any whitespace
      if (!is_numeric($this_cell)) $this_cell = '"'.$this_cell.'"'; //encapsulate in quotes if it is not a number
    $csv_newcells[] = $this_cell; //add it to the new cell array
    } //foreach cell
  $csv_report .= implode(',', $csv_newcells)."\r\n"; add the new cell line to the output
  } //foreach line
echo $csv_report;

Linode Xen to KVM upgrade breaks quotas

On a Linode Virtualmin CentOS 6 the upgrade from Xen to KVM breaks quotas with the following error:

repquota: Cannot stat() mounted device /dev/root: No such file or directory

The issue is that the symbolic link /dev/root is linking to /dev/xvda which has been replaced by /dev/sda so the symlink just needs to be replaced:

# rm /dev/root
# ln -s /dev/sda /dev/root

Then pop into Virtualmin (Webmin, System, Disk Quotas) and turn the quotas back on.

Fix nss-softokn rpm/yum issue in CentOS 6

The recent update to nss-softokn breaks rpm/yum updates in CentOS 6.

To restore functionality run these commands:

For 64-bit:

# wget http://mirror.centos.org/centos/6/updates/x86_64/Packages/nss-softokn-freebl-3.14.3-19.el6_6.x86_64.rpm
# rpm2cpio nss-softokn-freebl-3.14.3-19.el6_6.x86_64.rpm | cpio -idmv
# cd lib64
# cp libfreeblpriv3.* /lib64
# yum update

For 32-bit:

# wget http://mirror.centos.org/centos/6/updates/i386/Packages/nss-softokn-freebl-3.14.3-19.el6_6.i686.rpm
# rpm2cpio nss-softokn-freebl-3.14.3-19.el6_6.i686.rpm | cpio -idmv
# cd lib
# cp libfreeblpriv3.* /lib
# yum update