Running a professional blog for free
Tags: webdev, gatsby, ghost, react
Published: 2020-11-15 20:36:52

---
title: Running a professional blog for free
published: true
date: 2020-07-15 15:09:21 UTC
tags: #webdev #gatsby #ghost #react
canonical_url: https://www.jamify.org/2020/07/15/running-a-professional-blog-for-free/
---

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.

 ![Running a professional blog for free](https://www.jamify.org/static/4b7c9f9965fe24bca65b9daf7a09e270/photo-1443745029291-d5c27bc0b562.jpg)

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](https://ghost.org/pricing/).

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](https://ghost.org/pricing/). Even a relatively inexpensive solution that was described in the [Ghost CMS on Hetzner Cloud](https://www.jamify.org/2020/04/07/ghost-cms-on-hetzner-cloud) tutorial here on [Jamify.org](https://www.jamify.org) cost you at least [$3/month](https://ghost.org/pricing/).

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](https://www.netlify.com/) 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`](https://github.com/styxlab/gatsby-theme-try-ghost/tree/master/packages/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](https://nodejs.org/), the package manager [yarn](https://yarnpkg.com/en/docs/install#alternatives-tab) and the `ghost-cli`. Even if Node.js is already installed, check that you have a recent version:

```bash
$ 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:

```bash
$ 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:

```bash
$ mkdir ghost-local
$ cd ghost-local
```

Use the previously installed `ghost-cli` for performing the actual install:

```bash
[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/](http://localhost:2368/ghost/) and complete the setup process in three steps:

 ![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-cms-setup-step1.png)

 ![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-cms-setup-step2.png)

 ![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-cms-setup-step3.png)

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_

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-8.png)

and click _Create_ in order to generate a new key:

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-9.png)

### Starting and Stopping

The installation routine automatically starts the local Ghost server. You can stop it with the following command:

```bash
[ghost-local]$ ghost stop
```

You will also have to re-start your server after every reboot:

```bash
[ghost-local]$ ghost start
```

If you want to check whether or not Ghost is currently running you can use this handy command:

```bash
$ 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](https://github.com/styxlab/gatsby-starter-try-ghost):

```bash
$ git clone https://github.com/styxlab/gatsby-starter-try-ghost.git jamify-starter
```

and change into the work directory:

```bash
$ 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:

```json
{
  "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`](https://github.com/styxlab/gatsby-theme-try-ghost/tree/master/packages/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:

```bash
[jamify-starter]$ yarn remove gatsby-theme-ghost-members
```

and also exclude it in your `gatsby-config.js`:

```javascript
// 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`](https://github.com/styxlab/gatsby-theme-try-ghost/tree/master/packages/gatsby-rehype-inline-images) plugin:

```bash
[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:

```javascript
// 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`](https://github.com/styxlab/gatsby-theme-try-ghost/tree/master/packages/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](https://medium.com/). See the plugin [readme](https://github.com/styxlab/gatsby-theme-try-ghost/tree/master/packages/gatsby-rehype-inline-images) to learn more about the options this plugin provides.

## Smoke test

You can now start your first test build with

```bash
[jamify-starter]$ yarn develop
```

and see if the build succeeds. Check the results on [http://localhost:8000/](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:

```bash
[jamify-starter]$ yarn develop
```

and verify that the new article comes through:

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-new-article.png)

## 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:

```bash
[jamify-starter]$ yarn build
```

Check that your site looks as expected by starting the build server:

```bash
[jamify-starter]$ gatsby serve
```

and visit your site at [http://localhost:9000/](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](https://www.jamify.org/2020/05/19/getting-started-with-jamify/).

Here, I just repeat the simple steps. Once you have the Netlify CLI tool installed you can login

```bash
$ sudo npm -g install netlify-cli

[jamify-starter]$ netlify login
```

and deploy the previously build bundle from the `public/` folder.

```bash
[jamify-starter]$ netlify deploy --prod
```

Answer the upcomimg questions as follows:

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-12.png)

and you should see you site manually published on Netlify

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-13.png)

under [https://eager-golick-336605.netlify.app](https://eager-golick-336605.netlify.app) or another unique subdomain.

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-netlify.png)

Although optional, many people want to deploy their blog on a [custom domain](https://docs.netlify.com/domains-https/custom-domains/). 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](https://www.jamify.org/2020/04/14/continuous-content-publishing/), 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](https://github.com/adnanh/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`](https://github.com/adnanh/webhook/releases/download/2.7.0/webhook-linux-amd64.tar.gz)  [binary package](https://github.com/adnanh/webhook/releases), 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.

```bash
#!/bin/sh

yarn clean
yarn build
netlify deploy --prod
```

Make this file executable with:

```bash
[jamify-starter]$ chmod +x build-and-deploy.sh
```

You can now create the `hooks.json` in the same directory:

```json
[
  {
    "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:

```bash
[jamify-starter]$ webhook -hooks hooks.json -verbose -port 7000
```

You can now test this new hook by triggering the webhook with

```bash
$ 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:

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-14.png)

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/image-15.png)

### 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](https://unsplash.com/). Make sure to hit the _Update_ button after making these changes.

![Running a professional blog for free](https://cms.jamify.org/content/images/2020/07/localhost-cms-content-edit.png)

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.**

> [Do you want early access to Blogody, the brand new blogging platform that I am creating? Just sign-up on the new Blogody landing page and be among the first to get notified!](https://www.blogody.com/landing)

***

This post was originally published at [jamify.org](https://www.jamify.org/2020/07/15/running-a-professional-blog-for-free/) on July 15, 2020.