Joost Jansky
Posted on November 15, 2020
Ever wanted to run a blog site with zero costs? This tutorial explains how to do this in minutes with Jamify! If you follow along with the instructions in this tutorial, you'll have your own zero-cost setup by the end of this article featuring professional class speed, availability and security.
If you ever deployed a blog or website to the public internet, you know that running a live site usually incurs operational costs. For example, a managed blog site hosted on Ghost.org starts at $29/month.
You can considerably cut down such expenses by using a self-hosted solution, but a publicly reachable server is not free. A self-hosted solution typically costs around $5/month. Even a relatively inexpensive solution that was described in the Ghost CMS on Hetzner Cloud tutorial here on Jamify.org cost you at least $3/month.
As Jamify sites are static sites they can be deployed to a global CDN and do not require traditional servers. CDN deployments are extremely cost efficient, therefore you can benefit from the free tiers of Netlify or other providers and run your live site at no costs on a global CDN.
However, up until recently, you still needed at least a self-hosted Ghost CMS installation on a public server. Why? Because not all images were included in your Gatsby bundle, so they had to be served from your Ghost CMS! That's why you had to make sure your CMS is publicly reachable and available 24/7, depleting all the cost benefits of the static site approach.
With the new Jamify plugin gatsby-rehype-inline-images
you can now integrate all images into your static site bundle, so you do not need the CMS for serving images anymore. This opens up the possibility to deploy your blog from localhost for free.
Ghost CMS on localhost
Besides costs, being able to run your headless Ghost CMS on localhost has a couple of notable advantages:
- Easier setup as all production concerns fall away.
- No need to worry about security (firewalls, proxies, ports, authentication, SSL certificates, etc.).
- You can build your Jamify website offline and achieve fast build times that are not limited by network bandwidth (unless your blog incorporates external images).
Of course, a local CMS installation also has some limitations and cannot cover all use cases:
- Authoring articles with teams is not possible (no public access).
- Member functions (e.g. newsletter subscription and distribution) cannot be used (no publicly available backend).
Even if you need a public Ghost CMS installation at some point in time, a localhost installation is still the number one choice in a lot of testing scenarios.
Local CMS install
Let's start with installing a Ghost CMS on your local machine. The following instructions have been tested on a Linux system running on Fedora, but should work on other Linux flavours too, including MacOS.
If you are using Windows it is recommended to install the Windows Subsystem for Linux (WSL 2) and use Ubuntu 20.04 LTS.
Prerequisites
To install Ghost locally make sure you install Node.js, the package manager yarn and the ghost-cli
. Even if Node.js is already installed, check that you have a recent version:
$ node -v
v12.18.2
Node comes bundled with the package manager npm
. Make use of it right away and install yarn
and the ghost-cli
with it:
$ sudo npm -g install yarn ghost-cli@latest
Install Ghost
With the needed tools available, you can now create a new directory and install Ghost CMS in it:
$ mkdir ghost-local
$ cd ghost-local
Use the previously installed ghost-cli
for performing the actual install:
[ghost-local]$ ghost install local
It may take a minute to download and install all files. After a successful install, you can visit the CMS under http://localhost:2368/ghost/ and complete the setup process in three steps:
For a local install, you can safely enter a bogus email address and skip the staff user invite as you won't be able to work in teams anyway. After completing the final step, you should be brought to the Ghost Admin dashboard.
Create API keys
The initial setup is now complete. You can make further customizations in the admin interface, but this is not required at this point in time.
In other tutorials on Jamify.org you may read the recommendation to turn on headless mode. For a local installation this is not needed as the installation will never be available publicly, so you can skip this step.
In order to be able to connect to your Ghost instance with Jamify later, you need to generate a content API key. Go to Integrations -> Add custom integration
and click Create in order to generate a new key:
Starting and Stopping
The installation routine automatically starts the local Ghost server. You can stop it with the following command:
[ghost-local]$ ghost stop
You will also have to re-start your server after every reboot:
[ghost-local]$ ghost start
If you want to check whether or not Ghost is currently running you can use this handy command:
$ ghost ls
Keep the current Ghost server running if you want to follow along with this tutorial.
Importing Content
If you are already running another Ghost instance, you may want to import your content to your local installation. The easiest way is to use the import/export functionality under the Labs section.
Unfortunately, images must be transferred manually. So, in addition to the above import/export for the textual content, copy all images in directory content/images/
from the source to the target Ghost instance.
Local Jamify install
With a local CMS install it also makes sense to generate your static site locally, in fact, you won't be able to build your site with a cloud provider because your localhost CMS is unreachable for them. Let's download the Jamify starter:
$ git clone https://github.com/styxlab/gatsby-starter-try-ghost.git jamify-starter
and change into the work directory:
$ cd jamify-starter
Adding Keys
The Jamify starter must be told to source in the CMS content from you Ghost CMS on localhost. For that, create a new file called .ghost.json
in your work directory and copy the previously generated content API keys in it:
{
"development": {
"apiUrl": "http://localhost:2368",
"contentApiKey": "2a087eea8fc3c9a3e7392625c0"
},
"production": {
"apiUrl": "http://localhost:2368",
"contentApiKey": "2a087eea8fc3c9a3e7392625c0"
}
}
You must replace the
contentApiKey
with your own!
Remove members plugin
The Jamify starter includes some plugins for convenience, one of which is the gatsby-theme-ghost-members
plugin. As discussed earlier, the members plugin needs a public backend which currently must be a Ghost CMS, therefore the subscription flow won't work with a CMS on localhost. That's why you should remove this plugin:
[jamify-starter]$ yarn remove gatsby-theme-ghost-members
and also exclude it in your gatsby-config.js
:
// gatsby-config.js
plugins: [
//{
// resolve: `gatsby-theme-ghost-members`,
//},
]
Add plugin for inline images
While feature and all meta images are included in the static bundle by default, all inline images within posts or pages are only included if you add the new gatsby-rehype-inline-images
plugin:
[jamify-starter]$ yarn add gatsby-rehype-inline-images
This is a sub-plugin of the gatsby-transformer-rehype
plugin and must be placed into your gatsby-config.js
as follows:
// gatsby-config.js
plugins: [
{
resolve: `gatsby-transformer-rehype`,
options: {
filter: node => (
node.internal.type === `GhostPost` ||
node.internal.type === `GhostPage`
),
plugins: [
{
resolve: `gatsby-rehype-ghost-links`,
},
{
resolve: `gatsby-rehype-prismjs`,
},
{
resolve: `gatsby-rehype-inline-images`,
},
],
},
},
]
Including this plugin may result in longer build times as all inline images must be downloaded. However, as you are downloading them from localhost you are not limited by network bandwidth.
The gatsby-rehype-inline-images
plugin also processes images, so they are lazy loaded and fade in with a nice blur-up effect known from Medium. See the plugin readme to learn more about the options this plugin provides.
Smoke test
You can now start your first test build with
[jamify-starter]$ yarn develop
and see if the build succeeds. Check the results on http://localhost:8000/. Now, add a new article in your Ghost CMS on localhost and press the Publish button. After that re-build your project again with:
[jamify-starter]$ yarn develop
and verify that the new article comes through:
Build your static site
While the development command is great for testing, you need to issue the build command to generate an optimized bundle that can be deployed to a CDN:
[jamify-starter]$ yarn build
Check that your site looks as expected by starting the build server:
[jamify-starter]$ gatsby serve
and visit your site at http://localhost:9000/.
Deploy to Netlify
Now its time to deploy your site to a global content delivery network (CDN). This step has been described in more detail in the getting started tutorial.
Here, I just repeat the simple steps. Once you have the Netlify CLI tool installed you can login
$ sudo npm -g install netlify-cli
[jamify-starter]$ netlify login
and deploy the previously build bundle from the public/
folder.
[jamify-starter]$ netlify deploy --prod
Answer the upcomimg questions as follows:
and you should see you site manually published on Netlify
under https://eager-golick-336605.netlify.app or another unique subdomain.
Although optional, many people want to deploy their blog on a custom domain. You can either purchase a new domain on Netlify or bring your own. This does not come for free and a custom domain name will cost you about $1,5/month.
Congrats! 🎉 Believe it or not, your site is now running on a flaring fast CDN with zero operational cost. Plus, your site is even SSL secured and enjoys all the Jamstack security benefits.
Continuous Deployment
In another tutorial article, I showed how you can setup a continuous deployment pipeline, where a content change in your CMS triggers a site re-build which is then automatically propagated to your live site on the global CDN.
This setup heavily relies on online cloud services: your CMS must have a public endpoint that triggers a webhook on Gatsby Cloud which deploys the build to Netlify. With a CMS on localhost, this setup won't work anymore.
Webhooks on localhost
With a neat webhook open-source project from Adnan Hajdarević you can create a similar pipeline on localhost too. There are different methods available for installing this Golang program, here I choose to download the the webhook-linux-amd64.tar.gz
binary package, unpack the file and copy the executable in /usr/bin/
folder of my machine to make it globally available.
The webhook program consumes a hooks.json
definition file and spins-up a server on localhost. We need to define two actions:
- Build the jamify-starter with
yarn build
- Deploy the build package with
netlify deploy --prod
which can be combined in one script file called build-and-deploy.sh
that you should place in your work jamify-starter
directory.
#!/bin/sh
yarn clean
yarn build
netlify deploy --prod
Make this file executable with:
[jamify-starter]$ chmod +x build-and-deploy.sh
You can now create the hooks.json
in the same directory:
[
{
"id": "jamify-webhook",
"execute-command": "./build-and-deploy.sh",
"command-working-directory": "/home/styxlab/jamify-starter"
}
]
The working directory must be an absolute path, please change it to your own needs. You can now spin-up the webhook server:
[jamify-starter]$ webhook -hooks hooks.json -verbose -port 7000
You can now test this new hook by triggering the webhook with
$ curl -X POST http://0.0.0.0:7000/hooks/jamify-webhook
Connect to CMS
You can add this webhook easily to your CMS on localhost, so it get's triggered whenever you make changes to your content:
Test your pipeline
Let's make a simple content change. For this test, I change the title of the first post and also exchange the feature image with another one from Unsplash. Make sure to hit the Update button after making these changes.
After the build has completed, your live site should then be automatically updated with the new content changes.
Summary
If you do not need member functions and mostly write your articles yourself, the described setup may be exactly what you need for running an up-to-date, super-fast, SSL-secured, shell-proof public blog site with 99.99% uptime at zero costs 🥳.
While this article focuses on the operating costs of a live blog site, it is worth mentioning that this solution makes blogging not only more affordable, it also makes blogging more accessible : The install process is much simpler, so that it is feasible for people who feel discouraged by traditional self-hosting solutions.
Furthermore, taking a public CMS server out of the equation will certainly reduce complexity, ongoing maintenance efforts and let your mind put at easy: without a server, you cannot be hacked.
This post was originally published at jamify.org on July 15, 2020.
Posted on November 15, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.