The Journey to Ghost

Maybe not surprising, but I have my home server set up as a one-stop shop. I will do a write-up on this in detail. Short version: For the past couple months now I use my gaming rig to run UnRAID, which allows me to keep the a file server (NAS) running 24x7, run my main Windows environment as a VM, and run a Docker environment for hosting application all on the same bare metal hardware.

This will focus on the setup I have for hosting this blog: a Ghost docker container runs privately for writing and editing, I scrape static content using node.js, upload that content using Git to BitBucket, and hook in Netlify for hosting the static content to the web.

static-ghost-website-architecture

Let me break down the reasoning for the components:

  • Ghost: Light-weight blogging platform. Wordpress dominates the web for content hosting, and I do love it! However, it feels like an 800 pound gorilla--an old one. Ghost is a fresh, light breeze that's laser-focused on textual content. For hosting it would have been easier to shell out the $20/month for a Ghost(pro) blog hosted by Ghost, but I am a bit cheap and wanted the challenge of hosting static content.
  • Node.js: Open-source, cross-platform server-side runtime environment. Ghost is built on a node.js framework. I had originally thought of spinning up another docker with either Python or node.js, but since the Ghost docker already has a node.js environment it made sense to keep it contained. Using a simple npm command, I can install the necessary components for making a static site- website-scraper.
  • BitBucket: Web-based version control repository. This was two-fold: I like the idea of version control for this entire website, and I wanted to force myself to learn and use Git commands. I chose BitBucket because it allows me to create a private repository for free, opposed to GitHub which only has free hosting for public repositories.
  • Netlify: All-in-one platform for automating modern web projects--they call it drop-dead simple deployment workflow. I found Netlify from another article showing how to host static content. Netlify does some pretty amazing things: So, it's a CDN for hosting static content, at the core. However, it hosts this content by tapping into repositories in real-time, automatically. It has a slew of microservices that can make a site feel anything but static (taps into AWS Lambda functions??, forms, identify management). And, uh… it's free for my purposes.

The Code

The following follows very closely the Black Sand post on Static Ghost. I owe my entire approach to it!

Set up the Ghost Docker image

Access the Official Ghost Docker.
Install this however is best for your Docker setup. I have a GUI interface, but the YML looks like it covers everything. I uses the internal SQL lite setup and avoid the MySQL interface for ease; although, I do like the idea of the MySQL interface for better backups. That may be less necessary once we have the scraping and repo up and running.

My only real tip here is that you might want to capture the entire container path /var/lib/ghost. I mapped /var/lib/ghost/content and it's everything I need for a running site backup, but I am unable to edit the config or theme files unless I SSH into the instance.

Start Ghost

Install node.js and website-scraper

We will be installing the node components directly onto the Ghost Docker image. The reasoning is two-fold:

  1. Ghost runs on node so the framework is available by default in the image. All we are going to have to do is install the website-scraping components to get it running.
  2. We can co-locate the scraping on the same machine to make it faster and more streamlined.
    I also installed git on the instance so I could run the script and push the changes from the instance.

Steps to install and configure the static web scraper:

  1. Log into Ghost docker exec -ti ghost bash
  2. Update your apt-get apt-get update
  3. Install Git to upload from this image apt-get install git
  4. Install the website-scraper app npm install --save website-scraper
  5. Install additional package for saving static content npm install --save fs-extra
  6. Create directory for static output in the content directory mkdir static-ghost
  7. Create the static.js file that will scrape the content. Note, I didn't create this file, it was generously available from the previous guide. You will have to edit the siteUrl to match your system--this is required for the links to properly update:
const outputDir = './static-ghost';
const siteUrl = 'http://192.168.0.105:2368/';

console.log(`Removing output directory: ${outputDir}`);
var fs = require("fs-extra");
fs.removeSync(outputDir);

console.log(`Analysing site at ${siteUrl}`);
var scrape = require('website-scraper');
var options = {
  urls: [siteUrl],
  directory: outputDir,
  //scrape posts, not just index
  recursive:true,
  sources: [
    {selector: 'img', attr: 'src'},
    //most images are displayed using background-image
    {selector: 'header.site-header', attr: 'style'},
    {selector: 'figure.post-full-image', attr: 'style'},
    {selector: 'div.post-card-image', attr: 'style'},
    //find stylesheets
    {selector: 'link[rel="stylesheet"]', attr: 'href'},
    //and any scripts used
    {selector: 'script', attr: 'src'},
    //shortcut icon
    {selector: 'link[rel="shortcut icon"]', attr:'href'}
  ],
  //dont scrape external sites
  urlFilter: function(url){
    return url.indexOf(siteUrl) === 0;
  },
};

scrape(options).then( ()=> {
    console.log(`Static site generated under here: ${outputDir}`);
}).catch(console.log);

  1. Generate your static content! node static.js

Note: I had an issue with the domain remaining "localhost" for links in the footer, RSS feed button, and a few other buttons. To fix it, I had help from this Ghost setup tutorial. I needed to open /var/lib/ghost/config.production.json and edit the value "url": "http://<GHOSTIPADDRESS>:2368/".

Set up Netlify, Bitbucket, and Commit the site!

The final steps are to move this static content somewhere out on the world wide web! This will be accomplished by uploading to Bitbucket then linking to Netlify.
Create a Bitbucket account.
Create a Netlify account and link it to Bitbucket.

Commit your static files

From your Ghost Docker console, upload to BitBucket.

cd ./content/static-ghost
git init
git remote add origin https://<USERNAME>@bitbucket.org/<REPONAME>/static-ghost.git
git add -A
git config --global user.email "<USERNAME>@bitbucket.org"
git config --global user.name "<USERNAME>"
git commit -m "add static files"
git push origin master

Note: Mentioned in my First Post: I am a git newbie, and I had gotten stuck and deleted my entire content directory after I had it loaded to stage with a git reset --hard DO NOT DO THAT!! Apparently the better way is with a git rm command, but I am not all that familiar with it still.

Check out your site!

Netlify will automatically build based on your content. It may take a few minutes to do it the first time. Then you can attach your own domain and do all the other great features.