The Dark Side

I've been meaning to upgrade to Ghost v3 for months, but procrastinated over updating my theme to make it compatible.

I wanted some of the nice touches of the v3 flavour of Casper, the rather gorgeous theme that is enabled by default for new Ghost installs.

Given that my theme was already a customised Casper v2.11.1, I opted to re-add my modifications to a fresh copy of Casper v3.0.12. I then used the Gscan tool to validate my new theme as best I could.

The upgrade was initially deemed successful, but I soon discovered the automatic resizing to support responsive images was not working properly, when my first article after the upgrade weighed a hefty 5MB!  When a request for a resized image was made, Ghost was responding with a HTTP 302 direct to the original image - in all its glory - with terrible consequences for bandwidth and performance.

By coincidence, I'd already been mulling-over moving my blog from the rather crusty Raspberry Pi 2B, where it had been originally built, to a fresh VM on my (amd64) ESXI box.  
As much as I love Pis, it's been a bit of a struggle to update Ghost recently... I actually had three Ghost instances running on that poor little Pi:

  • bytes.fyi (this blog)
  • dev.bytes.fyi (my sandbox for trying out stuff; only accessible locally)
  • annholdsworth.co.uk (my wife's blog-to-be, which I've been diligently updating - and paying for the domain - for years and years now; she's never used it)

Using a new VM, I could allocate 2GB RAM, which would really help with running - and updating - three instances on the same box. Also, it would be a novelty to be running the supported stack for Ghost for the first time, err, ever...

So, with a slightly heavy heart and due sense of trepidation, I moved my blog(s) over to a new home.

The steps are listed below as an aide-mémoire for myself.


  1. Export articles as Json (Settings > Labs) from Ghost
  2. Backup the /var/www/ghost/content/images folder using tar
  3. Make a note of the existing settings (especially re: Mailgun config) from the config.production.json file
  4. Backup my Isso comments config and (sqlite) DBs using tar
  5. Backup my nginx config with tar
  6. Spin up a new Ubuntu 18.04 LTS VM and apply all updates
  7. The usual hardening: firewall, key-based auth (only) for SSH etc
  8. Install the latest and greatest Nginx, building from source
  9. Install MySQL, then do MySQL setup steps for Ghost
  10. Create a new user with no privileges to (install and) run Isso
  11. Install Isso comments
  12. Create services for comments.bytes.fyi and comments-dev.bytes.fyi, start and enable them
  13. Install Node.js 12 LTS via the recommended method
  14. Configure npm to enable sudo-less global package installs
  15. Update npm
  16. Install latest ghost-cli globally
  17. Create folders for each site and apply correct permissions
  18. Install Ghost using ghost install --no-setup-ssl (because I manage my Let's Encrypt certificate automation separately, on my TLS termination proxy), but don't start Ghost yet
  19. Repeat the previous step for each of the three Ghost instances, making sure that the port numbers are the same as those used on the Pi
  20. Use scp to copy over the backup config files
  21. Restore the backup config, then amend where needed (e.g. host IP in nginx config)
  22. Temporarily disable WAN access to my Ghost domains, for the initial setup of admin accounts
  23. Configure my reverse proxy to point 'dev' and 'ann' domains (and comments subdomains) at the new VM's IP
  24. Configure my ngx_pagespeed proxy to point to the new VM's IP
  25. Adjust domain mapping in my ngx_pagespeed nginx config, so origin images are grabbed from new VM's IP
  26. Fire up Ghost using ghost start, for each of the three instances
  27. Complete initial setup of admin users for each of the three blogs
  28. Import content from backup Json file, for each blog
  29. For bytes.fyi (and dev.bytes.fyi), I use ghostHunter search, so create a new Custom Integration called ghostHunter
  30. Update the JavaScript in my custom theme to use the Content Key from the new integration, then apply the updated theme
  31. Check everything works as expected: creating/amending/publishing articles, importing images, comments, search, etc...
  32. Restore public access to the blog domains