After experimenting with a basic Amazon EC2 instance for many months, I decided to migrate my server to Digital Ocean instead.

While Amazon EC2 was free and it was ok to experiment with for very basic sites, it quickly slowed to a crawl for anything other than static file serving. Also, the latency from the EC2 East region to my current home in Bangkok was atrocious, which was yet another reason to switch. After reading suggestions and reviews of VPS providers, I decided to give Digital Ocean a try.

Digital Ocean offers a basic instance (512MB / 1 CPU, 20GB SSD Disk, 1TB Transfer) at only $5 a month total, or billed hourly at less than 1 cent per hour if not used the entire month. They include a private and public IP address per instance, use SSD for storage, and have locations in San Francisco, New York City, Singapore and Amsterdam with a spinup time of less than a minute and a variety of Linux flavors available. They also support backups/restores, scaling and most of the features a typical site developer would need from a VPS provider, including a generous transfer limit and storage allowance at each price point. Basically, this blows away Amazon's EC2 micro instance in terms of performance and price so I gave it a shot.


Steps

(1) Start by setting up a new droplet (Digital Ocean uses this term for server instances):

Head over to Digital Ocean, enter an email address and a password and click on "Create Account". You'll be taken to the control panel immediately and a confirmation email will be sent to your address. Clicking on the link provided will enable full access to your account.

After confirming your email address, enter your billing information in the control panel. Once those steps are completed, you can create an instance and start experimenting by clicking on the "Create Droplet" button at the top of the screen. For my ghost blog, I chose the following settings to evaluate:

Size: I went with the smallest droplet: 512MB Ram 20GB SSD Disk

Region: I chose New York to make it a fair comparison against my EC2 instance.

Image: Ubuntu 13.10 x64 was my choice here as I'm already familiar with it from another VPS I setup recently.

Add optional SSH Keys: If you've setup images before, you should already ssh keys created and ready to use. If not, use this overview. If you choose this option, the root user login will be disabled on your droplet by default. You can re-enable by editing /etc/ssh/sshd_config using the tutorial here.

Settings: Currently three option checkboxes are available for new droplets.

  1. Enable VirtIO: This is enabled by default, and you should leave it on unless you have a good reason.

  2. Private Networking: This creates a private network interface for traffic between droplets in the same region without using your bandwidth allowance.

  3. Enable Backups: This will backup your droplet every few days in case something happens to it and you haven't made a snapshot recently. This will add 20% of the monthly cost but is worth it for most admins (In this example, I would pay $6/month for this droplet with backups enabled).

Hostname: Add a hostname to use for your droplet. Note: this should not be the FQDN, just the hostname.

(2) Click "Create Droplet" and wait a minute. Once your droplet has been created, you will be able to see the IP address it has been assigned and login to your new server instance.

NOTE: If you chose not to use an SSH key for login, your root password will be emailed to you by DigitalOcean. You should login and change this to a secure password of your choice.

OPTIONAL - You can make an SSH shortcut to make login easier. Edit your .ssh/config file and add the following lines:

      Host <SHORTCUTNAME>
           Hostname <SERVER IP OR DNS>
           User root

where the SHORTCUTNAME value is the droplet name or nickname you want to use, and the Hostname value is the IP or FQDN you've assigned to that IP in your DNS records. Test by running: "ssh SHORTCUTNAME" from your shell.

(3) Create a new user to work as that is not root and then assign that user a password. You can choose to do everything as root, but this is generally considered bad practice. As the root user, use the following commands:

 useradd -d /home/USER -m -s /bin/bash USER
 passwd USER

where USER is the username you wish to use. After adding the user, we change the password. You can also add an ssh key for this user to make logging in easier with the SSH config shortcut discussed before. For my ghost installation, I will be running it as this user, and adding it to the www-data group for permissions purposes.

(4) Update your installation to get the latest security fixes (this assumes you're using an Ubuntu flavor, otherwise use yum for RedHat-based releases):

 apt-get update
 apt-get upgrade

(5) Tighten security by doing the following as root user. Read this for more details on the steps I've used.

a) Add firewall rules: For Ubuntu 13.10 x64, "iptables" and "ufw" packages should already be installed and running. Type:

iptables --version

to see the iptables version installed and then

ufw version

to make sure UFW is also installed.

If both are installed and running, use the following commands to add some firewall rules:

ufw allow ssh
ufw allow http
ufw allow https

If you plan on receiving emails from outside your box, skip the next rule:

ufw deny smtp  

Make sure you've allowed SSH before enabling the firewall:

ufw enable   
ufw status verbose

If all is ok you should see the firewall rules you've just added.

b) Secure shared memory: This is to prevent a somewhat advanced attack technique, but can't hurt to add. Edit /etc/fstab and add the following line:

tmpfs     /dev/shm     tmpfs     defaults, noexec, nosuid     0     0

c) Add some network protection: Edit the "/etc/sysctl.conf" file and un-comment or add the following lines :

    # IP Spoofing protection
    net.ipv4.conf.all.rp_filter = 1
    net.ipv4.conf.default.rp_filter = 1
    # Ignore ICMP broadcast requests
    net.ipv4.icmp_echo_ignore_broadcasts = 1
    # Do not accept IP source route packets (we are not a router)
    net.ipv4.conf.all.accept_source_route = 0
    net.ipv6.conf.all.accept_source_route = 0
    net.ipv4.conf.default.accept_source_route = 0
    net.ipv6.conf.default.accept_source_route = 0
    # Do not send ICMP redirects (we are not a router)
    net.ipv4.conf.all.send_redirects = 0
    net.ipv4.conf.default.send_redirects = 0
    #Block SYN attacks
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_max_syn_backlog = 2048
    net.ipv4.tcp_synack_retries = 2
    net.ipv4.tcp_syn_retries = 5
    # Log Martian Packets
    net.ipv4.conf.all.log_martians = 1
    # Ignore Directed pings
    net.ipv4.icmp_echo_ignore_all = 1

and reload sysctl by running: "sysctl -p" once you're done editing the config file.

d) Install fail2ban to scan for ssh login attacks and automatically ban their ip's:

 	apt-get install fail2ban

Edit "/etc/fail2ban/jail.conf". Make sure "enabled=true" is set under the SSH section and port number is set correctly in case you're not using the default port 22.

Also update the "destemail = [email protected]" to your preferred email for receving warnings if you enable them.

Restart by running "/etc/init.d/fail2ban restart"

e) Setup an email service so you canget notifications from fail2ban and other local daemons to a remote email address:
If you're like me, you'll want to get emails sent to one of your main email accounts and not just stay on the server.
IMPORTANT: Make sure you have a FQDN setup on your ubuntu box. Make sure that DNS records have a FQDN that matches your boxes external IP.

Configure your MTA, which in my case is postfix.

emacs /etc/postfix/main.cf

And set the following:

myhostname = FQDN
mydestination = FQDN, localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128

where FQDN is your domain name for this droplet (ex: mynew.droplet.com).

If you have a gmail account you can look at this, otherwise you will need to do more work to setup s your new droplet aan authorized SMTP sender that won't get bounced as a spam source by most email providers.

For gmail users, make sure it includes the following settings:

relayhost = [smtp.gmail.com]:587
smtp_use_tls=yes 
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = 		hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/postfix/cacert.pem
smtp_use_tls = yes
smtp_tls_security_level = encrypt
inet_interfaces = all
default_transport = smtp
relay_transport = smtp

Create the file "/etc/postfix/sasl_passwd" and add the following, replacing [email protected] with your gmail address(@gmail or @DNS if you use Gmail Business with its own DNS) and PASSWORD with your password for that account:

[smtp.gmail.com]:587    	
[email protected]:PASSWORD 

Then run:

chmod 400 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd

Next, validate SSL certificates to avoid running into errors. Just run following command:

cat /etc/ssl/certs/Thawte_Premium_Server_CA.pem | sudo tee -a /etc/postfix/cacert.pem

Finally, reload postfix config for changes to take effect:

service postfix restart

f) Install a rootkit detection program, optional but can't hurt.

    apt-get install rkhunter 
    rkhunter --update
    rkhunter --propupd
    rkhunter --check

Run this periodically to be safe if you're afraid of root kits. Add it to a crontab and have it email you the output if you wish.

g) Install Nmap port scanner and check your firewall

apt-get install nmap
nmap -v -sT localhost   
nmap -v -sS localhost    

The first will check open TCP ports, the second will check open SYN ports, make sure all ports but the once you've opened in your firewall are closed.

h) Install logwatch to keep an eye on your log files.

apt-get install logwatch

To test, run the following and make sure you get the email specified in "--mailto":

logwatch --mailto [email protected] --output mail --format html --range 'between -7 days and today'

Edit /etc/cron.daily/00logwatch and add this command or a variation to receive daily emails from log watch.

/usr/sbin/logwatch --mailto [email protected] --output mail --format html --range 'between -7 days and today'

(6) Install git and nodejs.

apt-get install git
apt-get install node-js 
apt-get install npm 

NOTE: Ubuntu nodeJS package installs as "nodejs" and not "node" so run the following command:

ln -s /usr/bin/nodejs /usr/bin/node    

(7) Install nginx for web serving and proxying

apt-get install nginx

You may also want to setup a directory for storing websites to run with nginx:

mkdir /var/www
chown www-data:www-data /var/www

By default, nginx is set to run as user www-data in the main configuration file(/etc/nginx.conf). Update appropriately if you've chosen another user to run as.

(8) Start serving a site with nginx. We'll specify a new website to serve that will point to the default port for a NodeJS site:

emacs /etc/nginx/sites-available/ghost

Add following text to file:

	server { 
        listen 80;
        server_name localhost;

        location / {
                proxy_pass http://localhost:2368; 
                proxy_set_header Host $host;
                proxy_buffering off;
        }
	}

Then run the following to enable the new site, remove the default site file and restart the nginx service.

ln -s /etc/nginx/sites-available/ghost /etc/nginx/sites-enabled/ghost
rm sites-enabled/default 
service nginx restart 

If you get an error "nginx: [warn] conflicting server name "localhost" on 0.0.0.0:80, ignored", then change the "server_name" value from "localhost" to your IP address.

NOTE: At this point you should decide if you want to stay with the SQLite3 DB used by default with Ghost or switch to MySQL. Personally, I've decided to stick with Mysql as I'm more familiar with it.

OPTION 1: SQLite

a) Install sqlite:

apt-get install sqlite

b) Install ghost:

Get the Ghost install file set. Since we're using SQLite, no extra configuration is needed for the DB backend.

NOTE: From this point on, you should be logged in as the user you created for NodeJS purposes.

If you want an easy way to switch to root, you can do the following. As root, run "visudo". Add the following line, where user is the username you'll be using .

user ALL=NOPASSWD: /bin/su

Now you can switch to root at anytime by running "sudo su" as the user you created.

c) Complete the Ghost installation:

sudo apt-get install unzip
cd /home/user/ghost
wget https://ghost.org/zip/ghost-0.3.3.zip
unzip ghost-0.3.3.zip
cd ghost
npm install --production

Change "/home/user/ghost" to whatever directory you wish to serve your ghost site.

Edit your development and production server names(DNS) and ports to use for each version.

emacs config.js

Start your new ghost site:

npm start --production 

OPTION 2: MySQL

a) Install MySQL:

apt-get install mysql-client mysql-server

Choose a password for your root mysql user and enter it when prompted. Once installation completes, run the mysql CLI to setup the databases ghost will use.

mysql -uroot -p

Enter your root password when prompted. This will bring up the MySQL CLI, at which you will enter the following:
(Note: You'll have to create a mysql user account, so choose a password and replace in PASSWORD below before pasting.)

create database ghostdev;
create database ghost;
create user 'ghost'@'localhost' identified by 'PASSWORD';
grant all privileges on ghost.* to 'ghost'@'localhost';
grant all privileges on ghostdev.* to 'ghost'@'localhost';
flush privileges;
quit

b) Install ghost:
Get the Ghost install file set. Since we're using SQLite, no extra configuration is needed for the DB backend.

NOTE: From this point on, you should be logged in as the user you created for NodeJS purposes.

If you want an easy way to switch to root, you can do the following. As root, run "visudo". Add the following line, where user is the username you'll be using:

user ALL=NOPASSWD: /bin/su

Now you can switch to root at anytime by running "sudo su" as the user you created.

c) Complete the Ghost installation:

sudo apt-get install unzip
cd /home/user/ghost
wget https://ghost.org/zip/ghost-0.3.3.zip
unzip ghost-0.3.3.zip
cd ghost
npm install --production

Change "/home/user/ghost" to whatever directory you wish to serve your ghost site.

Edit your development and production server names(DNS) and ports to use for each version.

emacs config.js

d) Configure Ghost to work with mysql:

First install the npm libraries to talk to mysql.

npm install mysql

Then configure ghost to use MySQL instead of SQLite3.

emacs config.js

Find the "database:" json block inside the configuration file and replace the database blocks with the following code under both the development and production sections. Leave the "Testing" and "Travis" sections alone since these are more easily tested against the known local SQLite database in case of bugs while doing development on the ghost platform itself.

For the development section use:

    database: {
            client: 'mysql',
            connection: {
                host: 'localhost',
                user: 'ghost',
                password: 'PASSWORD',
                database: 'ghostdev',
                charset: 'utf8'
            }
        },

and for the production section use:

    database: {
            client: 'mysql',
            connection: {
                host: 'localhost',
                user: 'ghost',
                password: 'PASSWORD',
                database: 'ghost',
                charset: 'utf8'
            }
        },

Finally, update the development and production server names (DNS) and ports inside the config file.

(9) Time to make sure it works. Run:

 npm start --production 

Then browse to your IP (or your DNS if you've already configured it to point to this server's ip) and if everything is working you should see your new ghost site!

For my test sites, the latency difference dramatically improved compared to my old EC2 instance so I'll keep using Digital Ocean droplets for new sites I build in the near future.