A little while ago, at work, we realised we needed a new website. After some initial analysis, we quickly realised our options on a platform were somewhat limited due to conditions imposed on us by clients in their Vendor Risk Assessments; especially, that we not use PHP[1]. So, that pretty much rules out Joomla, Drupal, Weebly, and, yes, that bastion of blogging, WordPress, not to mention a slew of others.

Our CTO went out, did some research, and came back recommending the use of Ghost, which uses Node.js, and was, thus, within the rules we found outselves constained by.

Although Ghost is a blogging platform, it's easy to view a corporate website as mostly that; after all, what are news stories, press releases, product information, and so on, if not stories of one kind or another?

Ghost is also quick. You don't have to wait for oodles of processing on the server side. Ariba! Ariba!

I was tasked with setting the site up, which I immediately outsourced to an agency as I had no clue what I was doing. I chose the agency, not for their CSS skills, but their knowledge of Ghost. It turns out that there aren't many agencies that specialise in Ghost in the UK (at least, not yet), though I'm sure that will change over time. Indeed, every single agency I contacted tried to pursuade me to use a different platform such as WordPress or ProcessWire -- all good stuff, but outwith the remit.

Because I'm the curious type, I decided that, in tandem, I would learn Ghost. For one thing, I'm a bit of a secret, if not very talented, nerd; secondly, I believe that knowning how a platform works, helps one utilise it more effectively; and, thirdly, it stops others pulling the wool over one's eyes.

I started by trying to install Ghost on a linux server hosted with a well-known cloud provider, but due to their set up, was unable to (something for another time). That was a Friday, about three weeks ago. It just so happens, I stopped at the local pub on the way home. There, a friend with far more geek prowess than I'll ever muster said simply why not install Ghost on a Raspberry Pi.

Excellent idea, buy that man a beer!

Now, I can run my best-selling blogs from home and never pay hosting costs again[2]. Ta dah!

Right there and then a Raspberry Pi was purchased -- not off my friend (he didn't have a spare few hiding beneath his long raincoat with the unsold dodgy watches), but from a well-known, large online retail establishment. £50 (until the Rapsberry dies).

We interrupt this blogcast to bring you a disclaimer:
Disclaimer: The instructions below were figured out following lots of trial and error. They are almost certainly not the most succinct and I completely accept there may be better ways of achieving the same goal. If you decide to try some, or all, of the steps, you do so at your own risk. That being said, they did end up with me getting two Ghost websites being served by Nginx from one Raspberry Pi, so the result, whether arrived at expediently or otherwise, was the right one.

We now return you to your blogging pleasure:

Once I'd taken receipt of the Raspberry Pi, the first step was to expire the default password. I kept the default user, but haven't used that for setting up, instead creating a new one and granting it all the same rights. I won't cover this here; a simple Google on setting up a Raspberry Pi should suffice.

GhostPi instructions

Next, I followed the instructions at this, excellent, blog, at GhostPi, by Wes Archer, on how to install Ghost 1.x on a Raspberry Pi. However, whilst very good at getting Ghost up and running quickly, there are some points I feel could be clarified or that I would do differently, especially if you want to install more than one Ghost instance (for example, the website you're reading and StoryPositive both run on the same Raspberry Pi, using the same Nginx instance.

SSL Installation

I suggest ignoring the part where he bypasses the SSL installation, though I understand why he's done that. Later, he gives excellent advice on using Cloudflare to act as your Nameservers and for DNS. However, he's advocating the use of Cloudflare's Flexible SSL. In this scenario, your website is not SSL encrypted. What happens is traffic is routed to Cloudflare's servers, and then served data from your website that way. Thus, the connection between your server and Cloudflare is unencrypted, the connection between the requestor and Cloudflare is encrypted. Google won't notice and neither will myriad website visitors, but that still leaves a theoretical risk that someone bypasses Cloudflare and wreaks havoc on your site. Of course, you may decide that's a risk worth taking, in which case, fine, but given that Nginx can reissue valid Let'sEncrypt certificates every three months, why not use the Full SSL option from Cloudflare instead? That will ensure end-to-end encryption.


In his blog, Wesley Archer mentions the congig.json a few times. Indeed, this is also mentioned elsewhere. In my installation (which I believe is because the CLI installer has been modified since Wesley wrote his blog post), that file doesn't exist. Instead, the following appears to be used in its place: config.production.json.

Ghost directories

Because We is creating a single blog site, there's no need to separate out the blogs by having them pointed at specific directories. Therefore, you can get away with /var/www/ghost/ as being the directory you run from. But, because Ghost requires a differet instance to be runnning for each website, when you have more than one you will need to have a difrerent structure. So, by the end of this, I have:

/var/www/ghost/corballis-co-uk for the site you're reading; and
/var/www/ghost/storypositive-com for the StoryPositive website.

Script to keep IP Address up to date

I couldn't get his script to keep the IP Address up to date until I'd seeded the file that the script uses with an IP address. I think any will do.

Supplemental instructions to enable hosting of more than one Ghost blog on Raspberry Pi

Here, then, are the additional steps I undertook in order to get the second site https://storypositive.com/ up and running. Some of the below might've been circumvented by running the CLI installer, for the second time, in the /var/www/ghost/storypositive-com/ directory. But, that might not be the case and, from searching round the Internet, it's obvious I'm not the first numpty in the world, so here they are, in case you find yourself in this predicament.

Stop Ghost

  1. Switching to the Ghost diretcory: cd /var/www/ghost/
  2. Running the command to stop ghost from within there. ghost stop
    Note, you don't need to use sudo as it's already in the script that Ghost runs.

Stop nginx

You can do this from anywhere using the following command:
sudo /etc/init.d/nginx

Copy the Nginx config file for the original site (for the new site)

  1. Go to the Nginx sites available directory
    cd /etc/nginx/sites-available/
  2. Issue the copy command
    sudo cp <site1> <site2>```` e.g. sudo cp corballis.co.uk storypositive.com```

Here, we're copying the contents of the file for Corballis.co.uk to StoryPositive.com

Edit the Nginx config file for the second site

  1. I've used nano, but you can use whatever text editor you fancy, as long as it's installed:
    sudo nano storypositive.com
  2. Change the following:
    2.1 All instances of <site1> to <site2>
    2.2 The port number assigned in the file to the site:
    proxy_pass to proxy_pass http://127/0.0.1:xxxx
    where xxxx is any unused port number above 1024. I used 2368 (the default Ghost port, for corballis.co.uk) and 2369, but you can use whatever you want as long as it's not already in use for something else.
  3. Save and exit from nano.

Create the directory folder for the second site

  1. First, switch directory: cd /var/www/
  2. Next create the directory for the new site: sudo mkdir <site2>, e.g.
    sudo mkdir storypositive.com
    If you're planning on creating multiple sites, repeat for each.
  3. Make copies of the Ghost directory in each subdirectory (i.e. each site directory):
    3.1 sudo cp -r ghost <site1>, e.g.
    sudo cp -r storypositive.com
    3.2 Repeat for all new sites.

Remove the orginal Ghost file from the original Ghost directory

1 Either rename the ghost config file, e.g.
mv ghost ghost.ORIGINAL
or move as is to one of the new site directories, e.g.
mv ghost storypositive.com

Now edit the Ghost config file

Here, I'm assuming that, like me, you're using a config.production.json file for each site.

  1. In turn:
    a. Go to the directory for each new site: cd /var/www/<site>/ghost
    b. Open the file in a text editor, like nano: sudo nano config.production.json
    c. change the “url:” setting to the site name, e.g. http://storypositive.com
    If you're wondering why I'm stating 'http' instead of 'https' as I mentioned ealier that I thought one should ignore Wesley's advice on bypassing the SSL install for Nginx. Quite simply, I followed his advice to the letter at first, so here I don't have SSL configured (that's for a later date).
    d. change the port number to the one established earlier, e.g. 2369
    e. save and exsit from nano.

Ensure right privileges assigned to root directory

  1. Ensure that the logged in users, e.g. fred, owns the root directory:
    a. Change to the directory: cd /var/www/corballis.co.uk/
    b. Change owner: sudo chown fred:fred /var/www/corballis.co.uk/
    c. Check it's worked: ghost doctor from within that directory.
  2. Recreate the ghost users for each site:
    a. cd /var/www/<site>/
    b. ghost setup linux-user

Assign the right execution permissions

This is done at the root (for Ghost) level:
sudo chmod -R 755 /var/www

Edit the Nginx files

  1. Move to the right directory:
    cd /etc/nginx/sites-available/
  2. Edit the site config files.
    a. Here's what I did for corballis.co.uk:
    i. sudo nano corballis.co.uk
    ii. ensure all website references are to corballis.co.uk
    iii. save and exit
    b. Here it is again for StoryPositive:
    i. sudo nano storypositive.com
    ii. ensure all website references are to storypositive.com

It may be that this isn't necessary, but by now I didn't want to not do something only to have to then undo later work or find I'd broken something. So:

sudo rm corballis.co.uk.conf

As I say, I may be exposing the fact that I'm a bit of a novice, but hey-ho.

I did this for all appropriate SymLinks.

Note: Ensure not to put a / at the end of the file name.

Now, I'd managed somehow to name the Nginx file default to defaultOLD in my travels. It turns out that Nginx needs this, so I had to rename it back:

sudo mv defaultOLD default

This file tells Nginx which server to serve as a default and is used for nginx updates.

Having removed SymLinks to ensure that old, incorrect ones weren't hanging around, I next created the right ones.

  1. sudo ln -s /etc/nginx/sites-available/corballis.co.uk /etc/nginx/sites-enabled/
    This creates the correct symlinks for the first site (corballis.co.uk)
  2. sudo ln -s /etc/nginx/sites-available/storypositive.com /etc/nginx/sites-enabled/
    Creates the correct symlinks for the second site (StoryPositive.com)

We can check that the SymLinks are good, as follows:

  1. cd /etc/nginx/sites-enabled/
  2. ls
    You should now see the files from sites-available enabled here. If you ran this command before creating the symlinks, you should not have been able to see them.

Edit the Nginx config file

This step is recommended on lots of other sites; however, when I went into the file on my Pi it was already uncommented. I'm including it here in case my situtaion is unique.

  1. Change to the Nginx root directory:
    cd /etc/nginx/
  2. Edit the file:
    sudo nano nginx.conf
  3. Ensure the following line is uncommented:
    server_names_hash_bucket_size 64;
  4. Save and close

Restart Nginx

We're now ready to restart Nginx:

sudo systemctl restart nginx

Restart Ghost

Amd, we're ready to restart Ghost (one instance per website). First let's check that it's all good:

ghost ls

This produces output that tells you if the site is running or not. Go to any sites that aren't running and start Ghost for those sites. You have to do this from the actualy Ghost directory for each site, e.g.

cd /var/www/storypositive.com/


ghost start

Other problems

Because my second install hadn't worked fully, I had some file errors come up and the database for StoryPositive didn't exist (though the permissons within the database did exist, which was odd).

Fixing file errors

This was just a permissions error, not addresses above. To fix it, I ran the following command:

find ./ ! -path "./versions/*" -type d -exec chmod 775 {} \;

Creating the second website database manually

Because of the way I'd installed the second instance of ghost it turned out its database hadn't been created, though the users had been granted permissions to the nonexistent database (how odd, but again, hey-ho). This is how I resolved the issue

  1. Go into Maria:
    sudo mysql -u root -p
    The -p switch prompts for a password. You could also use -pmypassword which is the -p with the password appended.
  2. Create the missing database:
    CREATE DATABASE storypositive.com
    Note, these are SQL commands that are entered from within the Maria command prompt, not the linux prompt.
  3. Exit Maria: exit

Now we're going to dump the first (exsiting) database, in my case corballis.co.uk, and use that to seed the new one StoryPositive.

To do this, we first issue this command from the pi command interface (i.e. linux):

mysqldump yourFirstDatabase -u user -ppassword > yourDatabase.sql

The password in -ppassword is the Maria database one for the userid, not your linux/Raspberry Pi user's one.

Next, we use that dump as the seed by loading it in, but this time to the newly created StoryPositive.com database. From the linux prompt, issue this command:

sudo mysql yourSecondDatabase -u user -ppassword < yourDatabase.sql

Finally, check that it's all worked by going into Maria and running a test:

sudo mysql -u root -ppassword
show tables from data_base_name_com

You should be shown a long list of database table names. Personally, I don't think it's worth checking them too closely. If they look right they probably are and, if they aren't, you'll find out soon enough when you try to run your blog.


One issue with this approach is it copies all the data from your first site to your second, including all of the stories. If you only have one or two stories that's no great biggie as you can just delete them from the Admin console. This is also true for various site settings, but, again, easily remedied from the Admin console.

Start Ghost for the second site

Because the database wasn't originally there, the StoryPositive.com Ghost instance had failed to start. We can enable this in two ways: either by going to the StoryPositive Ghost directory (/var/www/ghost/storypositive-com/) and running ghost start or by running the following command (which is what I did):

systemctl enable ghost_storypositive-com.service

Check all Ghost instances again

And finally, let's check the ghost instances again:

ghost ls

You should see something like this:

How it all looks when both Ghost servics are up and running

There you can see that both instances are reported as running. The final check, of course, is to visit both sites. If they work, you know you've cracked it.

Additional changes

If you followed Wes Archer's advice and set up things like te script to keep Cloudflare up-to-date with the right IP Address for your sites, you will have to edit those to ensure you're doing it for both sites.

Good luck!

Useful references

  1. Of course, we could have simply opted to go through the process of being reassessed under client VRA processes, but when you have more than 35 large banks on your system that's no small process. Best, then, we thought to just get on with sticking to the rules. ↩︎

  2. You'll still need to sort out e-mail somehow, so you may not get away with never paying any other costs, but it still appealed to me. ↩︎