Deploy Ghost the Immutable Way on Clever Cloud

I am currently on a personal quest for a self hosted blog platform that just work. So might you see more posts about blog platform. And the first candidate I wanted to try is Ghost, since it’s advertised as the natural successor to WordPress(Because yes I don’t want WordPress, It’s too old and I am in tech so I just can’t).

Ghost is in their own words:

A fully open source, adaptable platform for building and running a modern online publication. We power blogs, magazines and journalists from Zappos to Sky News.

Sounds promising, let’s dig in. It’s written in NodeJS, and it requires MySQL. Should work right off the bat on Clever Cloud. That is if they follow an immutable approach to production deployment. And as it turns out, they don’t really do that. It’s even harder if you try to stick to recommended production tools and methods. So it might be a little more complicated than I thought at the begining. But it ended up working fine. Here’s how.

Your local blog installation

The first thing to do is create your blog locally. Ghost comes with a CLI tool you can install with npm or yarn. I am using npm so npm install -g ghost-cli. Then simply create a folder, change directory and install a Ghost blog:

mkdir myblog
cd myblog
ghost install local

You should see some logs and right at the end an invitation to go to http://localhost:2368/ghost/. This is your new blog. You should go through a configuration wizard to setup everything. It’s using the development configuration right now, with a local SQL lite database and logs written to disk. Now you can stop the local blog by running ghost stop.

If you list the content of your directory you should see the following:

[ldoguin@caolila:~/myblog]$ ls
content/  current@  versions/  config.development.json

All the specifics of your blog are stored in content. current is a symbolic link to the latest Ghost release downloaded in the versions folder. There is another symbolic link in content/themes/casper that points to the latest capser theme built in Ghost. This link will likely be broken once the blog is pushed to Clever Cloud because of the way we will install Ghost on it. So what you can do right now is replace that link with the actual content of the theme, and add a couple of other themes for good measure. It’s also the time to create our git repository. Because the new themes are going to be git submodules. This way it can easily be updated, while controlling the version.

rm content/themes/casper/
cp -r current/content/themes/casper/ content/themes/
git init
cd content/themes/
git submodule add https://github.com/curiositry/mnml-ghost-theme
git submodule add https://github.com/zutrinken/attila/

To test the new theme let’s start Ghost in debug mode: ghost run -D. If you go back to the admin page under http://localhost:2368/ghost/, click on the Design tab and you will see your new installed themes at the end of the page. Once you are done you can stop the server by simply hitting Ctrl-c.

Now you have a running installation locally, let’s see how to move it to the Cloud.

Your blog on Clever Cloud

To interact with Clever Cloud I recomment using our wonderful CLI clever-tools available easily on most distributions as you can see in our documentation. This is the tool I will use in the example. You can of course do the same thing using Clever Cloud’s Web Console.

In a terminal, inside your blog folder, run the following command:

clever create --type node myblog # create a node application
clever addon create mysql-addon --plan s_sml myblogsql # create a MySQL database
clever service link-addon myblogsql # Link the database to the application
clever env set NODE_ENV production # setup environment variable for production
clever env set CC_PRE_RUN_HOOK ./clevercloud.sh # run a specific script before the build phase
clever domain add mysuperblog.cleverapps.io # Add a test domain name

Now there is an existing application and a database on Clever Cloud, configured to run Ghost. But then we still need to create a bunch of files.

Installing Ghost for your Clever Cloud Application

You have seen in the previous step an envionment variable pointing to a pre run hook. It allows us run arbitrary code before the build phase. So let’s create that executable file with touch clevercloud.sh and chmod +x clevercloud.sh at the root of our repository. And as for the content here it is:

#!/bin/sh
npm install -g ghost-cli # install ghost-cli on Clever Cloud
mkdir ghost # create a folder for a new local instance of Ghost
cd ghost
ghost install local
ghost stop
cp ../config.production.json . # copy the production configuration

After that script we will have installed ghost-cli and the latest version of Ghost available. This script can be modified to have a specific version installed if you wanted to. But the thing is I like using upstream code: fail eary, fail often. Now on to the actual configuration.

Configure Ghost for Production

To configure Ghost you have two options. You can use a json file or environment variable. Here I am using a mix of two. Things that won’t change when deploying on Clever Cloud are going in the file. Things that may change, like the database used or the url, will be configured as environment variables.

Let’s start with the configuration file. You can copy the existing development file or create a brand new one with touch config.production.json. And here’s the content:

{
  "server": {
    "port": 8080,
    "host": "0.0.0.0"
  },
  "database": {
    "client": "mysql"
  },
  "mail": {
    "transport": "Direct"
  },
  "process": "local",
  "logging": {
    "level": "debug",
    "transports": [
      "stdout"
    ]
  },
  "paths": {
    "contentPath": "../../../content/"
  }
}

In this file you can see that the only logging transport used is stdout. As Clever Cloud uses Immutable infrastructure and might restart your application when needed, writing logs on file is pointless as they may be lost. Writing them in the standard output is what we advize and you can always configure log drains if you want to use a third party log management tool. Database client is rightly set to mysql. Something else that is changing from the developer configuration is the contentPath. It’s the path to your specific Ghost data, themes, assts, etc.. This is the folder where the themes are installed for instance. You can give an absolute or relative path. I’am using the relative path. And it starts in the running Ghost instance. In our case the Ghost version is installed by /clevercloud.sh in /ghost/versions/versionUsed/ and our content is available in /content/. So we need to go up three directories to make sure it founds our content folder.

And here is the list of environment variables. I have copy pasted the database values from the result of the clever env command.

clever env set database__connection__host btxgsb7xrhkuydlmb3zr-mysql.services.clever-cloud.com
clever env set database__connection__user ukf6bzfjgd2eg0iw
clever env set database__connection__password 87xRmGJljF3aSgGullm
clever env set database__connection__database btxgsb7xrhkuydlmb3zr
clever env set database__connection__port 20222
clever env set url https://mysuperblog.cleverapps.io/

At this point we have a properly configured Ghost instance using our own set of files. Now the question is how do you run it ?

Run Ghost

With node-js applications deployed on Clever Cloud we usually run the main or start field available in package.json. So what we can do is create it with touch package.json and add a start field that will run the blog with ghost-cli:

{
    "name": "ghost",
    "version": "0.1.0",
    "description": "",
    "scripts": {
        "start": "ghost run --dir ghost"
    },
    "devDependencies": {
    },
    "dependencies": {
    }
}

Here we have a configured, ready to run Ghost instance. To deploy on Clever Cloud we push code to a remote git branch. We need to do our first commit but first a bit of cleanup.

Ignoring local development files

There are a bunch of files we should not add to our git repository. Basically all the files related to our local deployment. So I added them to a .gitignore file and invite you to do the same.

.ghost-cli
config.development.json
current
versions

Git push to Production

Now you should have a fairly simple git add to do:

git add clevercloud.sh package.json config.production.json content
git commit -m "Initial commit"
clever deploy

If you go to https://mysuperblog.cleverapps.io/ghost/ you will run through the setup again, but this time storing things in the MySQL database. Take a look at the configuration options for Ghost, there are still a bunch of things you can do like setting up a proper email SMTP server.

And what about the traditional Production configuration from Ghost’s documentaiton like SSL Configuration, reverse proxy and such? Well the beauty of using Clever Cloud is that everything is taken care of for you. Nothing else to do.

And there you have it, your new blog is online. Deployed the immutable way.

Blog

À lire également

SuperBOL: The COBOL revolution in the Cloud

COBOL, a programming language that is over 60 years old, continues to power a large proportion of the IT systems of the world's major companies, particularly in the financial and insurance sectors.
Features

Clever Cloud welcomes the first startups to the UP Programme

Clever Cloud is proud to announce the arrival of the first five startups selected to join its UP Programme, an initiative dedicated to supporting young technology companies in their growth phase.
Company

A minor update resulted in a cascade of errors: how it went wrong, what we’ve learnt

On Friday, August 2nd, 2024 Clever Cloud’s platform became very unstable, leading to downtime of varying duration and scope, for customers using services on the EU-FR-1 (PAR) region, and remote zones depending on the EU-FR-1 control plane (OVHcloud, Scaleway, and Oracle). Privates and on-premise zones weren’t impacted.
Company Engineering