Serving Ghost blog as a static web application

Serving Ghost blog as a static web application

This site is built using Ghost (opens in new tab), a popular platform for blogging and media creators. This is a full stack application, with its own frontend and backend: users can subscribe to the Ghost SaaS offering, or self-host on a virtual machine/host. Until recently, my installation was hosted on a Digitalocean droplet (opens in new tab) (and I wrote quite extensively about it in previous posts). Now, what you are looking at is a fully static build of my blog.

This means that there’s no Ghost backend running at all times: I have a local Ghost instance, and when I change the site contents - such as writing this post - I then build it and commit my changes to a code repository on GitHub. Lastly, the new code build is pushed live and automatically served by my CDN using Cloudflare Pages (opens in new tab).

To clarify, this setup is not for everyone, since Ghost come with some features (such as paid membership) which require interaction with the Ghost backend at all times. In my case, I don’t have this requirement and as such this is a perfect set-up for me.

What are the advantages? Well, I can save on the hosting costs, since I do not need a virtual machine running at all times to run the Ghost platform and its database (MySQL in my case). Cloudflare Pages have a pretty generous free plan for personal sites such as this one, and a variety of competitive plans for those who have more requirements than mine.

Secondly, not having to run and maintain a backend saves me a lot of time, and significantly reduces the attack surface that I need to protect against.

Interested? Please read on to find out how to run your Ghost blog as a static web application.


Step 1: run Ghost locally

To set up this, you will still need to run your Ghost instance somewhere. This could be on your machine or, in my case, running it on a Home NAS such as a Synology device.

Most Synology devices can host and run Docker containers, so this step is quite simple: you can grab the official Ghost Docker image (opens in new tab) (I used ghost:5-alpine ) and spin up your instance. I won’t spend too much time on this step since I wrote quite extensively on this topic (opens in new tab) in the past. If you need more specific instructions, you can refer to this excellent article (opens in new tab).

I will however highlight some important notes:

  1. The officially supported (opens in new tab) database for production setups of Ghost is MySQL 8. However, in this setup, the Ghost installation is meant to be run locally so it won’t be subject to any load other than that created by the author of the posts. So, to avoid spinning up another container with MySQL 8, you can configure your Ghost instance to use SQLite instead. Just take care of mapping your container’s /var/lib/ghost/content folder with a persistent folder on your host, and pass the appropriate configuration environment variables ( database__client: sqlite3) to the container.
  2. For the container, remember to set up the url environment variable as well, to match the location where it will be reachable on the local machine. In my case, I used directly the IP and port that is mapped on my Synology for the Ghost container.
  3. If you are migrating away from an existing Ghost installation, make sure to export both your blog content and theme before winding down your existing setup. You can find the exporter under Settings > Labs in the Ghost admin interface, and you can download your theme as a zip file from the Settings > Design area.

Once you have started your Ghost instance, you can use it as normal, setting up the design and adding the required content. If you are coming from a pre-existing installation, you can now import your content and apply the template.

Finally, when it looks like you would expect, it’s time to build the static version of it and publish it on Cloudflare Pages!


Step 2: Create a GitHub repository and get the static exporter

In this step, we are going to create a repository on GitHub, which will keep the latest static snapshot of your website and which will be used by Cloudflare Pages to pull and deploy the latest version of your blog as new code is committed to it.

To build the static snapshot, we can use the ghost-static-site-generator (opens in new tab) tool which essentially crawls all the pages on the local copy and outputs the static version of it on your local machine. Once it has done this, we can push the output to our GitHub repo in readiness for our last step.

Assuming you have followed the instructions for the tool, and that your local ghost instance is running, you can then issue the following command on your machine:

gssg --domain http://192.0.2.1:8080 --url "https://www.example.com"

Where

  • --domain should point to your local Ghost instance
  • --url should be the actual, live url of your blog. This is because the gssg tool needs to substitute the local Ghost instance urls with the desired final value that will be used.

Once the tool has finished crawling the site, you will find the output in the /static folder created where you ran the command.

Now, you can go into that folder and initialize a Git repository, adding all the files, and committing them. Finally, you can add your GitHub repository created earlier as the remote, and push everything on GitHub.


In the final step, you will create a Cloudflare Pages project, link it to your GitHub repository, and add a custom domain for your Cloudflare Pages project so that you can serve your static content at the desired FQDN.

If you don’t have one already, create or log into your Cloudflare Account (opens in new tab). You can then refer to the following Pages documentation (opens in new tab) which tells you how to connect it to GitHub and your static blog repository.

You can then create your Pages project, by selecting the Connect to Git option and picking your repository. Give a name to the project, and select the main branch of your repo.

We can leave the rest unchanged…

Framework preset: 'None'
Build command: <empty>
Build output directory: <empty>

… and finally, press Save and Deploy. Cloudflare will pull, build and deploy your static version of Ghost, which will be available at <your-project-name>.pages.dev initially. Finally, you will need to go into the Custom Domains section and set up the actual domain you want to use for your site. If your domain DNS is also managed by Cloudflare, the setup is fully automated and the necessary DNS records will be created.

After a brief validation period, the custom domain will be enabled and voilá! You will be able to see your content online with super fast performance thanks to the Cloudflare CDN.


Step N: adding new content / modifying Ghost configs

From now on, if you add a new post on your local Ghost blog, modify the theme or similar actions, all you will need to do is to:

  1. Run the gssg tool again once you are done
  2. Commit the changes and push them to your GitHub repo

Cloudflare will see the updates in GitHub as soon as they are committed (or merged from a branch, if you prefer previewing your changes instead of going full cowboy) and deploy them automatically. Easy no?


Conclusion

I have changed the setup of my blog a couple of weeks ago and it has been pretty seamless. As I said, this setup is only viable if you do not rely on the dynamic features of Ghost such as memberships. I’ve also lost the ability to comment, which I had integrated using Commento (opens in new tab) - it requires a PostgreSQL database so it is not compatible with a purely static approach. But I am thinking about a substitute and I’ll probably write about it in a future post!

And, should I ever need to go back to a fully dynamic instance, it will be just a matter of re-exporting and re-importing the data from my local instance.

Happy blogging!