Self-hosting GHOST CMS with Google Cloud Platform, MySQL, sftp

All in one guide to create Ghost CMS on Google Cloud, with Cloudflare to set up a fast, functional, and affordable publishing platform.

Self-hosting GHOST CMS with Google Cloud Platform, MySQL, sftp

Ghost CMS is excellent for writers, bloggers, and professional publishers. It comes with a sleek & modern user interface that allows paid subscriptions for members. Compared to WordPress, Ghost wins overall on its loading speed, functionality, and affordability. These are important factors for the admin considering SEO, work productivity, and reducing overhead.

Before getting started, consider your circumstances. This guide would be suitable if you're starting from scratch or looking for alternatives to avoid monthly subscription fee services like DigitalOcean. You can get your domain from here and set up your Cloudflare here.

If you were using SQLite or looking to upgrade GHOST from a different infrastructure, this guide will not be suitable.

Prerequisite

  1. Domain
  2. Google Cloud Platform (free - low cost)
  3. Cloudflare
  4. Ghost Client v5.8x.x

Setting up an instance in Google Cloud Platform

The following stack will be installed onto an instance in the Google Cloud platform. Take note of the versions for each technology stack below. This will affect the setup process.

Stack overview

  • Ubuntu 20.04 or Ubuntu 22.04
  • NGINX (minimum of 1.9.5 for SSL)
  • Node Version: v18.20.3
  • MySQL 8
  • Systemd
  • A server with at least 1GB memory

Step 1: Set up your account

You'll need to sign up for a free account on the Google Cloud Platform. For new customers, you can benefit from the $300 credits for 90 days. The program coverage is only based on these free usage limits.

Visit this page for more details on how to set up your free google cloud platform.

Once you're in your GCP dashboard, create a new project and select your project.

Step 2: Enable Compute Engine

Navigate to Compute Engine
You'll need to enable Compute Engine API, and set up your billing profile.

Step 3: Create & set up a VM instance

Navigate to Compute Engine > VM instance.
Create your instance with the following and leave the others as default.

Machine Configuration

  • Series E2
  • at least e2-micro

Boot disk
Operating system:

  • Ubuntu 22.04 LTS
    x86/64, amd64 jammy image built on 2024-06-28

  • Boot disk type: Standard

  • Size: 30GB

Firewall
Allow HTTP traffic + Allow HTTPS traffic

Step 4: Update DNS with external IP address

Part of Ghost prerequisites is to set up the DNS record first.
You should update these records on Cloudflare so that SSL can be configured during setup.

A

name: yourdomain.com
value: external IP address

TXT

name: yourdomain.com
value: external IP address

CNAME

name: yourdomain.com
value: external IP address

Step 5: Set up Google Cloud instance

This part requires you to follow along step-by-step. By the end of step 5, you'll complete installing these within your Google Cloud instance.

  • Ubuntu 20.04 or Ubuntu 22.04
  • NGINX (minimum of 1.9.5 for SSL)
  • Node Version: v18.20.3
  • MySQL 8
  • Systemd
  • SSH Keys

The entire process takes about 1-2 hours depending on your internet speed.

Set up SSH keys

On your local machine, open up your terminal and key in this command.
Replace 'your_SSHkey_filename' with an SSH file name of your preference.

ssh-keygen -t rsa -b 2048 -f ~/.ssh/your_SSHkey_filename

Generate SSH keys on local machine

cat ssh-keygen -t rsa -b 2048 -f ~/.ssh/your_SSHkey_filename.pub

Command to print the SSH keys

Important: Copy the command you've used and SSH key on your local notepad, you'll need this later.

Go to your GCP instance

  • Click on the Edit button at the top.
  • Scroll down to the SSH Keys section.
  • Click on Add item and paste the public SSH key into the box.

Connecting to GCP instance

  • Go back on your local machine's terminal
  • Connect to your instance using SSH command

Update 'your_SSHkey_filename' , 'your_username', 'your_instsance_external_ip'

ssh -i ~/.ssh/your_SSHkey_filename your_username@your_instance_external_ip

Important: Copy the command you've used on your local notepad, you'll need this later.

If you're unclear about how to update - Here is an example.
Say you have the following details:

  • SSH file name is ghost-instance
  • Local machine username is admin
  • GCP External IP address of 123.123.123

    Your SSH command should look like this:
ssh -i ~/.ssh/ghost-instsance [email protected]

You should see results like below. Type 'yes' to accept into SSH.

Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Please type 'yes', 'no' or the fingerprint: yes

Once successful, you'll see something like this:

Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 6.5.0-1022-gcp x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information

  System load:  0.0               Processes:             100
  Usage of /:   6.4% of 28.89GB   Users logged in:       0
  Memory usage: 23%               IPv4 address for ens4: 11.111.0.11
  Swap usage:   0%
 

Now we can proceed with the next steps for setting up Ghost CMS on your Ubuntu 22.04 LTS instance.

Update Your System Packages

First, ensure that your system packages are up to date. Run the following commands:

sudo apt update
sudo apt upgrade -y

Install NGINX

  1. Install NGINX, which is required to serve Ghost CMS. Run the following commands:
sudo apt install nginx -y
  1. After installation, start and enable NGINX to ensure it runs on system startup:
sudo systemctl start nginx
sudo systemctl enable nginx
sudo ufw allow 'Nginx Full'

Review and Adjust Firewall Rules

  1. Check Existing Rules:
    First, let's list the existing firewall rules to see if there might be any conflicts or missing rules.
 codesudo ufw status verbose

Allow Essential Ports:
Ensure that the following ports are open:

  • HTTP (80)
  • HTTPS (443)
  • SSH (22)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp

Check and Adjust Incoming Traffic:
Allow incoming traffic for necessary services.

sudo ufw allow from any to any port 80,443 proto tcp
sudo ufw allow from any to any port 22 proto tcp

Allow Outbound Traffic:
Make sure that outbound traffic is not being restricted.

sudo ufw default allow outgoing

Apply the Changes: Enable the firewall with the new rules.

sudo ufw enable

Install MySQL

Ghost CMS requires MySQL. We'll install MySQL and set up a root password.

  1. Install MySQL Server:
sudo apt-get install mysql-server
  1. Create root user for MySQL

This command enters into mysql

sudo MySQL

Update permission for root user

  • Replace <your-new-root-password> with your desired password.
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '<your-new-root-password>';

Reread permission

FLUSH PRIVILEGES;

Exit sql

exit
  1. Verify MySQL
    To verify that MySQL is working correctly, you can log in to the MySQL server using the new root password you set up. Here are the steps to do so:

    Log in to MySQL:
mysql -u root -p
  • Enter your root password when prompted.
  • Check the MySQL version and show databases to ensure it is functioning correctly:
SELECT VERSION();
SHOW DATABASES;

Exit MySQL

exit

If you managed to successfully enter MySQL, you'll see a similar result like this

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 
Server version: 8.0.37-0ubuntu0.22.04.3 (Ubuntu)

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

Install Node.js

Next, we need to install Node.js, which is required to run Ghost CMS.
If you did not exit MySQL, please do so before running a new command.

  1. Install Node.js using NodeSource:
    This will install Node.js version 18.x, which is recommended for running Ghost CMS
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
  1. Verify the installation:
    Check that Node.js and npm (Node Package Manager) are installed correctly:
node -v
npm -v

These commands should display the installed versions of Node.js and npm without any errors. For example:

node -v
npm -v
v18.20.3
10.7.0

Install Ghost-CLI

Ghost-CLI is a command-line tool that helps in installing and managing Ghost CMS. You should still be SSH into your GCP instance when running this command. Let's install it globally into your GCP instance:

sudo npm install -g ghost-cli@latest

Install Ghost CMS

Now that Ghost-CLI is installed, we can proceed to install Ghost CMS. Change to the directory where you want to install Ghost (typically /var/www for production installations) and run:

# Create directory: Change `sitename` to whatever you like
sudo mkdir -p /var/www/sitename

# Set directory owner: Replace <user> with the name of your user
sudo chown <user>:<user> /var/www/sitename

# Set the correct permissions
sudo chmod 775 /var/www/sitename

# Then navigate into it
cd /var/www/sitename

Run the install process

Start the installation of Ghost with this final command:

ghost install

Your terminal will start the installation, it will look something like this:

✔ Checking current folder permissions
✔ Checking memory availability
✔ Checking free space
✔ Checking for latest Ghost version
✔ Setting up install directory
✔ Downloading and installing Ghost v5.85.1
✔ Finishing install process

During the installation, the Ghost CLI will prompt a few questions. You'll need to enter information about your site accordingly

? Enter your blog URL: https://yourdomain.com
? Enter your MySQL hostname: 127.0.0.1
? Enter your MySQL username: root
? Enter your MySQL password: [typed_password_is_hidden]
? Enter your Ghost database name: [your_database_name]
✔ Configuring Ghost
✔ Setting up instance

To initiate your website, run ghost start and ghost doctor to debug any issues

Ghost update

To update your Ghost blog, run ghost update. It takes about 10 minutes or less.

✔ Checking system Node.js version - found v18.20.3
✔ Ensuring user is not logged in as ghost user
✔ Checking if logged in user is directory owner
✔ Checking current folder permissions
✔ Checking folder permissions
✔ Checking file permissions
✔ Checking content folder ownership
✔ Checking memory availability
✔ Checking free space
✔ Checking for available migrations
✔ Checking for latest Ghost version

# 5.87.0

* 🎨 Updated editor toolbar and action button designs (#20405) - Sanne de Vries
* 🐛 Fixed default recipients setting not showing label filters (#20480) - Sag

---

View the changelog for full details: https://github.com/tryghost/ghost/compare/v5.86.2...v5.87.0

✔ Fetched release notes
☲ Downloading and updating Ghost to v5.87.0 > Installing dependencies > [3/5] Fetching packages...
View the changelog for full details: https://github.com/tryghost/ghost/compare/v5.86.2...v5.87.0

✔ Fetched release notes
✔ Downloading and updating Ghost to v5.87.0
+ sudo systemctl stop ghost
✔ Stopping Ghost
✔ Linking latest Ghost and recording versions
✔ Linking built-in themes
+ sudo systemctl start 
✔ Restarting Ghost
ℹ Removing old Ghost versions [skipped]

Access Ghost Admin Panel:

Open your browser and navigate to your Ghost admin panel URL (https://yourdomain.com/ghost) to set up your Ghost account.

SFTP into your Google Cloud Platform Instance

SFTP is another secure method to transfer files and allows more interactive file operations. This comes in handy if you prefer to manage your ghost files directly.

  1. Open a new terminal window on your Local Machine
  2. Get your SSH details, from your notepad
cat ~/.ssh/your_SSHkey_filename.pub

SSH into your GCP instance and edit the ~/.ssh/authorized_keys file for the admin user:

nano ~/.ssh/authorized_keys

Paste the contents of your local your_SSHkey_filename.pub file into this file and save it.

  1. Verify SSH Configuration

Check your SSH configuration file (~/.ssh/config) to ensure your gcp-instance alias is correctly defined. Open the file using a text editor:

nano ~/.ssh/config

Ensure the configuration for your gcp-instance looks like this:

Host gcp-instance
    HostName 123.123.123.8  # Replace with your GCP instance IP address
    User your_gcp_user
    IdentityFile ~/.ssh/your_SSHkey_filename
  • HostName: Should be the actual IP address of your GCP instance, not the placeholder 123.123.123.8.
  • User: Your username on the GCP instance.
  • IdentityFile: Path to your private key (your_SSHkey_filename).

Save Changes and Exit:

After verifying and updating if necessary, save the changes (Ctrl + O, then Enter) and exit (Ctrl + X) the editor.

  1. Access SFTP Connection

Once you've verified the SSH key setup, retry the SFTP connection:

sftp your_SSH_username_from_earlier@gcp_instance

Again, using the previous SSH details above as an example it will look something like this:

sftp admin@gcp-instance

Enter the passphrase for your SSH key if prompted. If everything is set up correctly, you should be able to connect without encountering the "Permission denied (publickey)" error.

Potential issues you may encounter

  1. During the 'ghost install' process, if your connection decides to drop or takes too long to load at any point, you can approach the issue with either of the solutions:
  • Increase RAM
    Stop your GCP instance, 'edit' the Machine Configuration to e2 small or medium for more RAM.
  • Partition MySQL with file Swap
    Detailed steps are documented in this Ghost forum discussion
  1. For some reason, you had to reinstall the Ghost CLI due to interruption and ghost install or ghost setup does not work. You'll need to remove the existing Ghost CLI with these commands below:

You can either execute this command using the terminal in your local machine while SSH into your GCP instance or SSH in-browser from your GCP instance.

This command will recursively remove all files and subdirectories

sudo rm -rf /var/www/your_folder_name/*

Recreate the Directory:
After cleaning up, recreate the /var/www/your_folder_name directory and ensure it's owned by your user:

sudo mkdir -p /var/www/your_folder_name
sudo chown $USER:$USER /var/www/your_folder_name

This ensures that the directory has the correct ownership and permissions for your user.

Retry Ghost Installation:

Navigate into the /var/www/your_folder_name directory and attempt to install Ghost again:

cd /var/www/your_folder_name
ghost install
  1. Issues connecting to your MySQL database during the Ghost installation.
Access denied for user 'admin'@'localhost' (using password: YES)

This typically means that the MySQL credentials (username or password) provided during the Ghost installation do not match what MySQL expects. Here’s what you can do to troubleshoot and resolve the issue:

Verify admin User Existence:

  • Ensure that the admin user exists in MySQL and has the correct privileges for the your_database_name database.
USE mysql;
SELECT User, Host FROM mysql.user WHERE User='admin';

If the admin user does not appear, you may need to create it and grant appropriate privileges:

CREATE USER 'admin'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON your_database_name.* TO 'admin'@'localhost';
FLUSH PRIVILEGES;

Verify Privileges for admin:

  • Double-check that the admin user has been granted all necessary privileges on the your_database_name database:
SHOW GRANTS FOR 'admin'@'localhost';

Ensure that the output includes privileges such as SELECT, INSERT, UPDATE, DELETE, etc., for the your_database_name database.

Verify MySQL User Privileges:

  • Ensure that the MySQL user (admin) has sufficient privileges on the Ghost database (your_database_name). The user needs permissions like SELECT, INSERT, UPDATE, DELETE, etc.
  • You can verify and grant privileges if needed by logging into MySQL:
mysql -u root -p
GRANT ALL PRIVILEGES ON your_database_name.* TO 'admin'@'localhost' IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;

Restart Ghost:

  • After verifying and correcting any issues with MySQL credentials or privileges, restart Ghost:
sudo systemctl restart ghost_your_domain-com

Update Ghost Configuration:

  • After verifying and ensuring the MySQL user (admin) and its privileges, update your Ghost configuration to reflect these credentials:
  • Typically, the Ghost configuration file (config.production.json or similar) should have these details. Ensure that the database section in your Ghost configuration file matches the MySQL setup:

To update the Ghost configuration file (config.production.json) with the correct MySQL credentials (admin user), follow these navigation steps:

  1. Access Your Server: Ensure you are logged into your server where Ghost is installed. If you are not already there, navigate to the directory where Ghost is installed. Us, Ghost is installed in /var/www/your_folder_name/.
cd /var/www/your_folder_name/
  1. Locate the Ghost Configuration File: In most cases, the Ghost configuration file is named config.production.json. It should be located in the root directory of your Ghost installation (/var/www/your_folder_name/ in this case). Look for a file named config.production.json or something similar.
  2. Edit the Configuration File: Use a text editor to open the config.production.json file for editing. You can use nano, vim, or any text editor of your choice. Here, we'll use nano as an example:
sudo nano /var/www/your_folder_name/config.production.json

This command opens the config.production.json file in the nano editor with superuser privileges (sudo) to ensure you have permission to save changes.

Navigate to the Database Section: Inside the config.production.json file, locate the database section. It typically looks like this:

{
    "url": "https://yourdomain.com",
    "server": {
        "port": 2368,
        "host": "127.0.0.1"
    },
    "database": {
        "client": "mysql",
        "connection": {
            "host": "127.0.0.1",
            "user": "admin",
            "password": "your_password",  // Update this field with the correct password
            "database": "your_database_name"
        }
    },
    "mail": {
        "transport": "Direct"
    },
    "logging": {
        "transports": [
            "file",
            "stdout"
        ]
    },
    "process": "systemd",
    "paths": {
        "contentPath": "/var/www/your_folder_name/content"
    },
    "bootstrap-socket": {
        "port": 8000,
        "host": "localhost"
    }
}

Steps to Update:

  1. Password Update: Replace "your_password" in the "password" field with the actual password for the admin MySQL user
  2. Save Changes: After updating the password, save the config.production.json file. If you're using nano, press Ctrl + X, then Y to confirm changes, and Enter to write the filename.
  3. Restart Ghost: Restart the Ghost service to apply the changes:
sudo systemctl restart ghost_yourdomain-com

Verify Operation:

Check the Ghost logs (journalctl -u ghost_yourdomain-com -n 50) for any errors or warnings to ensure that Ghost starts correctly with the updated MySQL credentials.

Test Your Installation:
Visit your blog URL to ensure that Ghost is running and functioning properly with the updated configuration.

By following these steps, you should successfully update your Ghost configuration.