How to install and setup Ghost CMS on Ubuntu

Ghost is a free and open source blogging platform written in Javascript, which saw its first release in 2013. It supports writing posts both using a WYSIWYG (What You See Is What You Get) editor, and the Markdown language. Unlike WordPress, it is focused on simplicity and on being purely a blogging platform, therefore it includes SEO and and social sharing features out of the box. Ghost offers a ready-to-go hosting service, Ghost(Pro), but can be easily self-hosted.

In this tutorial we see how to install and setup the Ghost blogging platform on Ubuntu, both in development and production mode.

In this tutorial you will learn:

  • How to quickly install Ghost in development mode manually or using Docker
  • How to setup Ghost in production
  • How to setup UFW to allow traffic through ports 80 and 443
How to install and setup Ghost CMS on Ubuntu
How to install and setup Ghost CMS on Ubuntu
Software requirements and conventions used
Category Requirements, Conventions or Software Version Used
System Distribution independent
Software NodeJs 14.x or 16.x, npm, Docker (if using the Ghost docker image)
Other Administrative privileges
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command $ – requires given linux-commands to be executed as a regular non-privileged user

Introduction

Ghost can run in development or production mode. Development mode is what we want to use when we need to quickly create a project or, for example, develop a theme. In this mode sqlite is used as database, and the installation of a dedicated web server is not needed. In production mode, instead, a proper stack must be installed: the officially supported one includes MySQL 8 as a database and the NGINX web server.



One requirement common to both installation modes is Node, since Ghost is written in Javascript and runs on the server-side Node.js engine. Supported Node versions are 14.x and 16.x. Chances are that on the platform we are running those versions are not available, therefore we must obtain them from third-party software sources: in this case we will use NodeSource.

Installing the latest Node.js LTS version

The latest Node.js version is 16.x; let’s see how we can install it on Ubuntu from the NodeSource repositories. The first thing we need to do is to download the NodeSource repository signing key, de-armor it, and copy it into the /usr/share/keyrings directory:

$ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/nodesource.gpg > /dev/null

As a next step we add the NodeSource repositories to our software sources. We create the /etc/apt/sources.list.d/nodesource.list file, and write the following content in it:

deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x jammy main 
deb-src [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource/com/node_16.x jammy main

Next, we synchronize the repositories and finally install the nodejs package. The node package manager (npm) will be installed as a dependency:

$ sudo apt-get update && sudo apt-get install nodejs

Installing Ghost in development mode

Let’s see how to install Ghost in development mode. The first thing we want to do is to install ghost-cli, an utility which is designed to help manage our Ghost installation.

Installing ghost-cli and Ghost

To obtain the latest version of ghost-cli, we must use npm. Often, the suggested way to install a package globally with the node package manager is to run the “npm install” command prefixed with sudo. This, however, is not a good idea, since arbitrary code can be executed as part of a package installation, and we don’t want that code to run with administrative privileges.



An alternative is to run npm with the --prefix option. This option takes a path as argument, and is used to specify where packages should be installed. In this case is enough to install the utility just for our user, therefore we can install it in our HOME; in the ~/.npm/ directory, for example:

$ npm --prefix=~/.npm install ghost-cli@latest -g

The command above installs the “ghost” binary as ~/.npm/bin/ghost. To avoid having to invoke the utility by its full path, we can add the ~/.npm/bin directory to our PATH. Once installed, we can use the ghost utility to deploy Ghost. First we create the directory that will host our project, than we install Ghost inside of it:

$ mkdir ghost-project && ~/.npm/bin/ghost install local --dir ghost-project

Ghost will be installed in local/development mode. Once the installation is complete, we should visualize a message similar to the following:

Ghost was installed successfully! To complete setup of your publication, visit:

        http://localhost:2368/ghost/

Visiting the page, we are be prompted to provide some basic project information:

The Ghost "welcome" page - here we can finalize our installation
The Ghost “welcome” page – here we can finalize our installation

If for some reason we want to uninstall Ghost, we can use the “uninstall” command of the ghost utility:

$ ~/.npm/bin/ghost uninstall --dir ghost-project

To start and stop the Ghost instance, instead, we can use the “start” and “stop” commands, respectively.

Installing Ghost locally using Docker

An alternative, very convenient option we can use to quickly start working on a Ghost project is by using the Docker image provided by the ghost community. In this section of the tutorial I assume you are already familiar with Docker. To build the Ghost docker image and launch a container based on it, we can use the following command:

$ sudo docker run -d --name ghostproject -e NODE_ENV=development -p 2368:2368 ghost



By running the command above, the “ghost” image will be downloaded (if it doesn’t exist on our machine), than a container based on it will be launched. Since we used the -d option the container will start in detached mode (it will run in the background, not blocking the terminal). With the --name option, we specified the container name (“ghostproject” in this case – a random one would be generated otherwise): we can use it as a quick and human-friendly reference. With the -e option, instead, we defined the value of the NODE_ENV environment variable: it is used inside the container to establish the environment mode. Finally, we used the -p option to map port 2368 on the host system to the same container port; this way, we will be able to access the “dockerized” Ghost instance by navigating to: http://localhost:2368/ghost.

Deploying Ghost in production

Until now we saw how to quickly install Ghost in development mode. When we need to deploy Ghost in production, we must setup MySQL8 as a database and use NGINX as a web server (this is the recommended setup). The official documentation suggests to perform the installation on one of the following Ubuntu versions:

  • 16.04
  • 18.04
  • 20.04
  • 22.04

For the sake of this article we will use the latest Ubuntu LTS version (22.04), since in this version MySQL8 is available in the default repositories.

Installing MySQL and NGINX

Installing MySQL8 and NGINX on Ubuntu 22.04 is a very easy operation. All we have to do is to run the following command:

$ sudo apt install mysql-server nginx

As part of the installation both the mysql and nginx services will be started and enabled at boot.

Creating a database for Ghost

We now need to create a database for our project. The first thing we need to do, is to login into MySQL shell:

$ sudo mysql -u root

From the MySQL shell that will be opened, we can issue the following queries to create a database for our project (“ghostproject”) and a database user (“ghostuser”) with all the privileges on it:

mysql> CREATE DATABASE ghostproject;
mysql> CREATE USER 'ghostuser'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON ghostproject.* TO 'ghostuser'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> quit;

Setting up the project directory

The next step consists in the creation of the project directory. Since we are deploying ghost in production, we will create it in the /var/www directory:

$ sudo mkdir /var/www/ghost-project



Next, we want to give the ownership of this directory to a standard user (“egdoc”, in this case). This user must be someone other than “ghost”, since the “ghost” user and group will be created as part of the installation by ghost-cli:

$ sudo chown egdoc:egdoc /var/www/ghost-project

At this point the directory for our project is ready, but we didn’t install the ghost-cli utility, yet. As suggested in the official documentation itself, the easiest way to perform the installation is to invoke the following command:

$ sudo npm install ghost-cli@latest -g

The command above would install ghost-cli in the /usr/local/bin directory which is already in our user PATH. Running npm with administrative privileges, as we already said, is a bad habit, therefore we can use --prefix to specify an installation directory. This directory must be accessible to the ghost user, since it is the user the automatically created systemd service will run as. For the sake of this example we will create a directory inside /opt, and call it “ghost”:

$ sudo mkdir /opt/ghost && sudo chown egdoc:egdoc /opt/ghost
$ npm --prefix=/opt/ghost install ghost-cli@latest -g

Once the utility is installed, we can use it to setup Ghost:

$ /opt/ghost/bin/ghost  install --dir /var/www/ghost-project

As part of the installation process some checks will be performed, than the needed dependencies will be downloaded and installed. Finally, we will be prompted to enter some information. Among the other things we will be asked to enter the URL of our blog (here I just used “ghostproject.lan”), the MySQL server hostname and the database name, username and password:

? Enter your blog URL: http://ghostproject.lan
? Enter your MySQL hostname: localhost
? Enter your MySQL username: ghost
? Enter your MySQL password: [hidden]
? Enter your Ghost database name: ghostproject



The script will than asks us also our “sudo” password in order to perform certain operations which require administrative privileges:

  1. Create the “ghost” system user and group (user will be created with the –system option, so its UID will be < 1000)
  2. Recursively assign the ownership of the “content” project subdirectory (/var/www/ghost-project/content in this case) to the ghost user and ghost group
  3. Asks us if we want it to automatically create an NGINX VirtualHost for the project
  4. Asks us if we want it to automatically setup SSL using Let’s encrypt
  5. Asks us if it should create a systemd service unit for the project

At the end of the process, we will be asked if we want to start Ghost: this could take a while, since the necessary tables will be created in the database. Once ready, the blog will be accessible at the specified URL. Here is the NGINX VirtualHost file created for this project:

server {
    listen 80;
    listen [::]:80;

    server_name ghostproject.lan;
    root /var/www/ghost-project/system/nginx-root; # Used for acme.sh SSL verification (https://acme.sh)

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;
    }

    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}

The one below, instead, is the generated systemd service needed to automatically start (and re-start) the ghost project (/lib/systemd/system/ghost_ghostproject-lan.service):

[Unit]
Description=Ghost systemd service for blog: ghostproject-lan
Documentation=https://ghost.org/docs/

[Service]
Type=simple
WorkingDirectory=/var/www/ghost-project
User=998
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node /opt/npm/bin/ghost run
Restart=always

[Install]
WantedBy=multi-user.target

Setting up the firewall

If we have a firewall running on our machine (we really should have!) we need to authorize traffic through the needed ports, which in this case are port 80/tcp and 443/tcp if we setup SSL. In the example below, I assume UFW (Uncomplicated Firewall) to be in use, since it is the default in Ubuntu. The command we need to run is:

$ sudo ufw allow "Nginx Full"

Conclusions

Ghost is a free and open source blogging platform built with Javascript. In this tutorial we learned how to quickly install a local instance of Ghost in our system for test and development purposes and how to deploy a Ghost blog in production. If you want to know more about the platform, you can simply visit the project official website.



Comments and Discussions
Linux Forum