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 Atlantic.net 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:

1
2
sudo apt-get update
sudo apt-get upgrade -y

Install Node.js:

1
2
3
4
5
6
7
sudo aptitude install -y build-essential zip vim wget
wget http://nodejs.org/dist/node-latest.tar.gz
tar -xzf node-latest.tar.gz
cd node-v*
./configure
make
sudo make install

Install Ghost:

1
2
3
4
5
6
7
8
9
sudo mkdir -p /var/www/
cd /var/www/
sudo wget https://ghost.org/zip/ghost-latest.zip
sudo unzip -d ghost ghost-latest.zip
cd ghost/
sudo npm install --production
sudo vim config.example.js
#leave host as 127.0.0.1 # we don't want the server to respond to anything but the nginx proxy
#change url to http://www.timothy-quinn.com

Create an account to run the Ghost Blog:

1
2
3
4
5
6
7
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
# server_name www.timothy-quinn.com;
# location / {
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header Host $http_host;
# proxy_pass http://127.0.0.1:2368;
# }
#}

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:

1
2
3
4
5
6
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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:
AU
ACT
Canberra
timothy-quinn
Blog
www.timothy-quinn.com
# don't set an email address
sudo vim /etc/nginx/conf.d/timothy-quinn.conf
add:
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.