
Ghost is a blogging platform that makes starting a blog easy. Write blog posts and grow your readers through one platform. Ghost can hosting this for you but they also give the user the ability to do so as well. This is the route I've decided to take.
Along with self hosting Ghost, I've also created my own theme (WIP). I want to style this blog my way and in the future I may want to change small parts of it. I like the fact that everything in Ghost is customized by handlebar files instead of React (like Gatsby). I have a lot more confidence handlebars staying around another 10 years then Gatsby staying around with all the great alternatives that exist now-a-days.
But I digress, so, like any good developer, I set off and stood up Ghost on my Raspberry pi home lab. I created a repo to house my template and created a GitHub action to upload the theme anytime there was a push to master. Ghost provides users with a helpful getting started guide and I was able to use that as a template. Within 5 minutes, I had my custom theme on my self hosted Ghost website. Awesome!!!
There was one small issue though...My website kept loading after I visited the website...
Loading for an entire 9 seconds! Impressive
I thought it would leave at some point and so I left it. I wasn't in a rush either as I don't expect anyone to visit the site right now. So there it sat until today, after my friend visited the site and asked the question:
Why is the site still loading after like 1 second?
Ugh, well, fine, if you are going to visit the site, I guess I'll figure it out ☠️ lo and behold, the culprit! livereload.js?snipver=1

Hmm, now why is my production blog site trying to load this livereload.js
file? I thought I was doing a production build and then uploading it to my site. I got most of my automation through their getting started guide. The action that TryGhost
even provides a beautiful GitHub action for this purpose, which you can read below:
name: Deploy Theme
on:
push:
branches:
- master
- main
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Deploy Ghost Theme
uses: TryGhost/action-deploy-theme@v1
with:
api-url: ${{ secrets.GHOST_ADMIN_API_URL }}
api-key: ${{ secrets.GHOST_ADMIN_API_KEY }}
Isn't that beautiful, they removed all the logic so that deploying a theme to a instance of Ghost is dead simple and straight forward. However, it doesn't answer the question of why livereload.js
is being uploaded? Maybe some google searches will lead to an answer.
Surfing the web
First we go to the source of my theme template, TryGhost/Starter
where there is an issue about this exact problem. It was recently posted 5 months ago. Sweet! But sadly it was closed. Damn, no leads.
My second thought was that maybe there was a way to tell Ghost's GitHub action to do a production build. I'll spare the details of the javascript action because it's simple, but the script is basically packaging up the project as is, without building it, and uploading that zip file to the server where it will be published. Therefore, you would still have the livereload.js
call to your production server because there is no build step.
Finally, I stumbled upon the issue documented in GitHub action repo TryGhost/action-deploy-theme
where other's discussed the problem. Crazy that this is the first issue. Although it's not surprising as the repository readme doesn't have any information about the topic!
The issue recommends to build around the upload action, which, in hindsight, makes sense. Using the recommendation in the issue, I copy, pasted and updated the code snippets into what I use today.
# Learn more → https://github.com/TryGhost/action-deploy-theme#getting-started
name: Deploy Theme
on:
push:
branches:
- master
- main
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Cache node modules
uses: actions/cache@v4.0.2
with:
path: node_modules
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.OS }}-build-${{ env.cache-name }}-
${{ runner.OS }}-build-
${{ runner.OS }}-
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.0.3
with:
node-version-file: .nvmrc
- run: npm install
- name: Build theme
env:
NODE_ENV: production
run: npm run zip
- name: Deploy Ghost Theme
uses: TryGhost/action-deploy-theme@v1
with:
api-url: ${{ secrets.GHOST_ADMIN_API_URL }}
api-key: ${{ secrets.GHOST_ADMIN_API_KEY }}
file: alecdivito-website-ghost-theme.zip # Note: This maybe different for you.
In Conclusion
Turns out that self hosting Ghost isn't as easy as it seems. There are some gotcha's that aren't clearly documented (or I'm not reading all of their getting started documentation haha). However, I now have a working landing page that doesn't take (technically) 8 seconds to load, and to me, thats a win!