How to setup a Ghost Blog with SSL on Ubuntu

This blog is running my fork of Ghost. I decided to use Ghost for this blog because datememe is also written in nodejs so I've got some experience with it.

While Ghost is missing some basic features like scheduling posts, commenting and proper SSL support this functionality shouldn't be too hard to add myself. I really like the markdown editor and the code is pretty nice to work with so lets get started.

Getting Ghost to work for me

I already had a nodejs app running on my root domain so I wanted to run Ghost using the same runtime and in the directory blog from my root domain. There were a few issues I needed to work out to get ghost working for me:

  • production install from git

  • setup in a directory

  • using unix domain sockets

  • let's encrypt ssl all the things

  • add commenting

  • setup with upstart

  • same runtime as datememe

  • use Bugsnag for error reporting

Installing Ghost from git

Here are the steps i used to install ghost on my production box from git.

git clone https://github.com/radiofrequency/Ghost /var/www/dm_blog
cd /var/www/dm_blog
npm install -g grunt-cli

I'm using Node 5.5 in my production environment so i needed to make some changes to package.json to get everything working correctly. Just change engines to include your Node version.

engines: {
  node: "~0.10.0 || ~0.12.0 || ^4.2.0 || ^5.5.0"
}

In my environment NODE_ENV=production so i needed:

npm install --dev  

Now you can run finish the install

grunt init
grunt prod
cp config.example.js config.js

Your blog is ready to be started once you edit the config.js file with your url etc.

Install in a directory

You don't want your blog on a subdomain. Throwing it in a directory will increase your search rankings. Here is the config required for nginx to load your ghost blog in a directory:

location ^~ /blog {
    proxy_read_timeout      600s;
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        x-forwarded-for $remote_addr;
    proxy_set_header        X-Queue-Start "t=${msec}000";
    proxy_set_header        X-NginX_Proxy true;     
    proxy_set_header        X-Forwarded-Proto https;    
    proxy_pass              http://dm_blog;
    proxy_redirect off;
}

Unix Domain Sockets

I like to always run nodejs apps using domain sockets rather than binding to ports as unix domain sockets are much faster and less risky from a security standpoint. Here is the relevant nginx config that defines the upstream to the ghost domain socket:

upstream dm_blog {
  server unix:/var/run/dm_blog.sock;
}

Modify your config.js and change:

server: {
      socket: '/var/run/dm_blog.sock'
}

Your ghost blog is configured to listen on a domain socket.

Commenting

While building out my own commenting would be trivial lots of sites use Disqus and it looks good with the default ghost theme so I just added that using the installation instructions for ghost here.

Upstart Script

I run ubuntu on VPS Dime and it uses upstart scripts found in /etc/init. Here is the upstart script i use for my blog.

description "dm blog"

start on started redis
stop on shutdown
expect fork

setuid www
env NODE_ENV="production"
env SPIN_SLEEP_TIME="60000"

chdir /var/www/dm_blog

script
    exec /var/www/dm_blog/node_modules/forever/bin/forever start --spinSleepTime 30000 --pidFile /var/run/dm-blog.pid -l /var/log/dm-blog.log -a -d /var/www/dm_blog/index.js 
end script

Save this file as /etc/init/dm_blog.conf.

You will need to add forever for this to work:

 npm install forever --save

Now you can start your blog with:

sudo start dm_blog

When your VPS reboots it will wait for redis to start then start your blog.

SSL

Really there is little excuse for not using SSL, it's free and easy to provide basic encryption protecting your users from malicious service providers or governments that would like to change your content before its delivered to the client.

Let's Encrypt

Setting up SSL is so easy with Let's Encrypt. I followed this tutorial to auto regenerate certificates every 60 days and configure nginx to use them.

Nginx

To force my sites to use ssl I use nginx to redirect any http traffic to https with a directive like this:

server {
    listen 80;
    server_name datememe.com www.datememe.com;
    return 301 https://$server_name$request_uri;
}

Ghost

The setup for SSL on Ghost is weird. If you set your url in config.js to use https it puts the blog in a redirect loop. After digging through the code i found you are supposed to specify urlSSL in your config like so:

 url: 'http://www.datememe.com/blog',
 urlSSL: 'https://www.datememe.com/blog',

This will allow the software to work but Ghost will still use the http url for meta tags like canonical url etc. To fix that you need to modify the code in:

core/server/data/meta/canonical_url.js

Just change:

    return config.urlJoin(config.getBaseUrl(false),
    getUrl(data, false));

to:

    return config.urlJoin(config.getBaseUrl(true),
    getUrl(data, false));

Passing true to getBaseURL will return the https url instead of the http url.

Now when your articles are shared you will not have a costly redirect from http to https slowing your blog load time.

Bugsnag

What good is your blog if it doesn't work? I need to install bugsnag on the client and server to make sure stuff is working and to be notified if any unhandled exceptions happen. So here are the steps to set it up on the client and server.

Client Side

If your new to Ghost you might not have a clue where to add the bugsnag code to the ghost project to get it to work. You want to add your bugsnag cdn script to the head:

The file you want to modify is:

content/templates/casper/default.hbs

Add your bugsnag cdn script tag:

<script
  src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-2.min.js"
  data-apikey="YOURAPIKEY">
</script>

Before {{ghost_head}} around Line 21.

Server Side

First you'll need to install the bugsnag module.

npm install bugsnag --save

Now you'll need to modify some code:

core/server/index.js

To the very top of the file add the line:

var bugsnag = require("bugsnag");

In the init function on Line 63 add the following:

bugsnag.register("YOURAPIKEY", {
  releaseStage: process.env,
  notifyReleaseStages: ["production"]
});

Restart your blog:

sudo restart dm_blog

Build your Blog

I hope these tips help you out when setting up your blog. If you haven't decided on a host for your blog I recommend VPS Dime. A VPS with 6GB of ram for 7$ a month is more than enough to host your Ghost blog and handle lots of traffic. This setup is also easy to move to Amazon or Google if you want to use auto scaling.

Up Next

I'm still not finished with modifying my ghost blog. A few issues i'd like to tackle in future blog posts are:

  • Scheduling Posts

  • Accelerated Mobile Pages

  • Better Structured Data

  • Address bugs found by Bugsnag

  • Develop a plugin

  • Theme creation

If your struggling with something on your own Ghost blog let me know in the comments and maybe i can help.

Continue Reading

Datememe is a new 100% free online dating site. Just because it's free doesn't mean you can't enjoy premium features found on other paid websites. No fake users, no paid features, just free online chat to help you meet that someone special. What are you waiting for?