How to actually build a Ghost Blog on Ubuntu 14.04

I’ve tried a few different ways to make this blog, and not all of them successful. I really have beginner level experience with Linux, Node.js and nginx so I probably made some stupid mistakes. I succumbed to the AWS marketplace to get my first instance up and running because it was zero configuration (click and giggle).

However, since announced $1/month VPS’ I decided to get my blog off of AWS and onto something that’ll run for much cheaper. It is just a blog after all.

Now I’ve personally found a lot of the guides online are really good, but they never seem to work when you follow them verbatim. There’s always one little bit that just doesn’t work, and so you’re left 99% of the way there scratching your head.

So I’m putting what I did here. This is everything I did to get my blog alive and on the web.

# Provision Atlantic Ubuntu Server 14.04 Machine
# Get Username and Password from Email sent from Atlantic
# Log into server via SSH using the Public IP provided

Install server updates:

sudo apt-get update
sudo apt-get upgrade -y

Install Node.js:

sudo aptitude install -y build-essential zip vim wget
tar -xzf node-latest.tar.gz
cd node-v*
sudo make install

Install Ghost:

sudo mkdir -p /var/www/
cd /var/www/
sudo wget
sudo unzip -d ghost
cd ghost/
sudo npm install --production
sudo vim config.example.js
leave host as # we don't want the server to respond to anything but the nginx proxy
change url to

Create an account to run the Ghost Blog:

sudo adduser --shell /bin/bash --gecos 'Ghost application' ghost
sudo chown -R ghost:ghost /var/www/ghost/
su - ghost
cd /var/www/ghost/
npm start --production
# The Blog should start running
# CTRL+C to kill the process

Install an nginx proxy:

su - root
sudo apt-get install nginx -y
cd /etc/nginx/conf.d/
touch timothy-quinn.conf
vim timothy-quinn.conf
# set the config file to read like this:
server {
    listen 80;
    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;

cd ..
sudo vim nginx.conf
# change 'include /etc/nginx/sites-enabled/*;' to '#include /etc/nginx/sites-enabled/*;' (comment it out)
# if you don't do the above then the nginx server will respond instead of forwarding to the ghost server.
sudo service nginx restart

Install pm2 to allow the ghost instance to run without an open console to the server:

sudo npm install pm2 -g --unsafe-perm
su - ghost
echo "export NODE_ENV=production" >> ~/.profile
source ~/.profile
pm2 kill
pm2 start /var/www/ghost/index.js --name ghost

Install a self-signed certificate and configure nginx to enable HTTPS:

su - root
sudo mkdir /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt
# cert details requested:
# don't set an email address
sudo vim /etc/nginx/conf.d/timothy-quinn.conf
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
sudo service nginx restart

Everything should work now! Try browsing to http://site-public-ip and https://site-public-ip to see if it works.

You can now point your domain name to the blog, and also employ some security hardening to keep the bad guys out. At least make sure you change the root password first, as you’ve got a copy of it sitting in your email from the provisioning.

I also turned on forcing SSL for the logon page, but I foolishly didn’t write down how I did it. I’ll try to add that to this list for the future. Don’t even think about logging into your blog over plain http, as that will expose your username & password to anybody who may be listening on the connection at the time.