(deprecated, see https://community.opalstack.com/d/1291-howto-run-the-latest-ghost-with-a-private-mysql-instance-on-opalstack for current procedure)
As of v5.21, Ghost is no longer compatible with MariaDB and will not run with SQLite in production mode - it will only run with MySQL v8 or higher. Opalstack doesn't provide a managed MySQL service at this time so if you want to run the latest Ghost you'll need to run your own private MySQL instance as a custom app.
The guide that follows is a modified version of a procedure originally written by @nick. The main differences are:
- The shell commands have been broken up into discrete numbered steps.
- The various config files and scripts are created using heredocs.
- Start and stop scripts are included for the MySQL app.
- Node.js is installed in a subdirectory "node" of the Ghost app with all modules installed globally.
- The Ghost instance is installed in a subdirectory "ghost" of the Ghost app.
- Email configuration is included for Ghost.
The guide will set up 2 applications: a MySQL server running on a custom port and a Ghost instance. The various commands use the following placeholders which you'll need to replace with your own values:
GHOSTUSER
: shell user nameGHOSTAPP
: nginx proxy app 1GHOSTPORT
: the port assignment for GHOSTAPPDBAPP
: nginx proxy app 2DBPORT
: the port assignment for DBAPPDBAPPROOTPWD
: mysql root user passwordGHOSTDBNAME
: ghost database nameGHOSTDBUSER
: ghost db user nameGHOSTDBUSERPWD
: ghost db user passwordYOURDOMAIN.COM
: domain to use for the appSMTPSERVER
: your Opalstack SMTP server, either smtp.us.opalstack.com or smtp.de.opalstack.com.MAILBOX
: your Opalstack mailbox nameMAILBOXPWD
: your Opalstack mailbox password
- Create a new shell user, e.g. GHOSTUSER
- Create two NGINX proxy port apps GHOSTAPP and DBAPP, noting the assigned ports for each.
- Add your site domain.
- Create a site to serve GHOSTAPP on YOURDOMAIN.COM with Let's Encrypt SSL enabled.
- Creata a mailbox and email address.
- SSH to your server using shell user GHOSTUSER. The rest of the procedure is presented as commands run as your shell user.
-
Create the MySQL config file:
cd ~/apps/DBAPP mkdir -p {etc,var,tmp} cat << EOF > ~/apps/DBAPP/etc/my.cnf [client] port = DBPORT socket = /home/GHOSTUSER/apps/DBAPP/var/mysql.sock [mysqld] port = DBPORT socket = /home/GHOSTUSER/apps/DBAPP/var/mysql.sock tmpdir = /home/GHOSTUSER/apps/DBAPP/tmp datadir = /home/GHOSTUSER/apps/DBAPP/data innodb_log_group_home_dir = /home/GHOSTUSER/apps/DBAPP/data log_error = /home/GHOSTUSER/logs/apps/DBAPP/maria.log pid_file = /home/GHOSTUSER/apps/DBAPP/var/pid sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION [mysqld_safe] log-error = /home/GHOSTUSER/apps/DBAPP/maria.log pid-file = /home/GHOSTUSER/apps/DBAPP/var/pid EOF
-
Download and setup MySQL:
cd ~/apps/DBAPP wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.33-el7-x86_64.tar.gz tar xzf mysql-8.0.33-el7-x86_64.tar.gz --strip-components=1 ~/apps/DBAPP/bin/mysqld --defaults-file=/home/GHOSTUSER/apps/DBAPP/etc/my.cnf --initialize-insecure --user=$USER --datadir=$PWD/data --tmpdir=$PWD/tmp
-
Start the MySQL instance:
nohup $HOME/apps/DBAPP/bin/mysqld_safe --defaults-file=$HOME/apps/DBAPP/etc/my.cnf --socket=$HOME/apps/DBAPP/var/mysql.sock > $HOME/logs/apps/DBAPP/nohup.out 2>&1 &
-
Login, set the root password, and create the Ghost DB+user:
~/apps/DBAPP/bin/mysql -P DBPORT -S $HOME/apps/DBAPP/var/mysql.sock -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'DBAPPROOTPWD'; create database GHOSTDBNAME; create user 'GHOSTDBUSER'@'localhost' identified by 'DBAPPUSERPWD'; grant usage on *.* to 'GHOSTDBUSER'@'localhost'; grant all on GHOSTDBNAME.* to 'GHOSTDBUSER'@'localhost'; FLUSH PRIVILEGES"
-
Create the start and stop scripts:
cat << EOF > ~/apps/DBAPP/start #!/bin/bash trap '' HUP nohup /home/GHOSTUSER/apps/DBAPP/bin/mysqld_safe --defaults-file=/home/GHOSTUSER/apps/DBAPP/etc/my.cnf --socket=/home/GHOSTUSER/apps/DBAPP/var/mysql.sock > /home/GHOSTUSER/logs/apps/DBAPP/nohup.out 2>&1 & EOF chmod +x ~/apps/DBAPP/start cat << EOF > ~/apps/DBAPP/stop #!/bin/bash /home/GHOSTUSER/apps/DBAPP/bin/mysqladmin -u root -P DBPORT -S /home/GHOSTUSER/apps/DBAPP/var/mysql.sock -p shutdown EOF chmod +x ~/apps/DBAPP/stop
-
Make a note of the following commands which you will use to start and stop the database server when needed:
- Start:
~/apps/DBAPP/start
- Stop:
~/apps/DBAPP/stop
- Start:
-
Get and build Nodejs v18.12.1 from source (this can take some time to complete):
cd ~/apps/GHOSTAPP/ mkdir node && cd node wget https://nodejs.org/dist/v18.12.1/node-v18.12.1.tar.gz tar zxf node-v18.12.1.tar.gz cd node-v18.12.1 scl enable devtoolset-11 -- ./configure --prefix=$HOME/apps/GHOSTAPP/node scl enable devtoolset-11 -- make -j2 scl enable devtoolset-11 -- make install
-
Export for Node PATH, update NPM and install some Ghost requirements via SCL:
export PATH=$HOME/apps/GHOSTAPP/node/bin:$HOME/apps/GHOSTAPP/node/node_modules/.bin:$PATH scl enable devtoolset-10 -- npm install -g npm@9.8.1 scl enable devtoolset-10 -- npm install -g @vscode/sqlite3 scl enable devtoolset-10 -- npm install -g ghost-cli@latest
-
Install the Ghost instance:
mkdir ./ghost && cd ./ghost ~/apps/GHOSTAPP/node/node_modules/.bin/ghost install local --port GHOSTPORT --log file --no-start
-
Replace the sqlite3 package:
rm -r ~/apps/GHOSTAPP/ghost/current/node_modules/sqlite3 cp -r ~/apps/GHOSTAPP/node/node_modules/@vscode/sqlite3 ~/apps/GHOSTAPP/ghost/current/node_modules/
-
Create the Ghost config file for production, disable default config files:
cat << EOF > ~/apps/GHOSTAPP/ghost/config.production.json { "url": "https://YOURDOMAIN.COM", "server": { "host": "127.0.0.1", "port": GHOSTPORT }, "database": { "client": "mysql", "connection": { "host": "127.0.0.1", "port": DBPORT, "user": "GHOSTDBUSER", "password": "GHOSTDBUSERPWD", "database": "GHOSTDBNAME" } }, "paths": { "contentPath": "/home/GHOSTUSER/apps/GHOSTAPP/ghost/content/" }, "logging": { "level": "info", "rotation": { "enabled": true }, "transports": [ "file", "stdout" ], "path": "/home/GHOSTUSER/logs/apps/GHOSTAPP/" }, "mail": { "transport": "SMTP", "options": { "host": "SMTPSERVER", "port": 587, "secure": false, "auth": { "user": "MAILBOX", "pass": "MAILBOXPWD" } } } } EOF find ~/apps/GHOSTAPP/ghost/versions/*/core/shared/config/env/ -type f -name "*.json" -exec sh -c 'mv "$1" "${1%.json}.json-backup"' _ {} \;
-
Create start and stop scripts:
cat << EOF > ~/apps/GHOSTAPP/start #!/bin/bash PATH=~/apps/GHOSTAPP/node/bin:\$PATH NODE_ENV=production ~/apps/GHOSTAPP/node/bin/ghost start -d ~/apps/GHOSTAPP/ghost --no-setup-linux-user EOF chmod +x ~/apps/GHOSTAPP/start cat << EOF > ~/apps/GHOSTAPP/stop #!/bin/bash PATH=~/apps/GHOSTAPP/node/bin:$PATH ~/apps/GHOSTAPP/node/bin/ghost stop -d ~/apps/GHOSTAPP/ghost --no-setup-linux-user EOF chmod +x ~/apps/GHOSTAPP/stop
-
Start Ghost:
~/apps/GHOSTAPP/start
-
Immediately visit your Ghost admin URL https://YOURDOMAIN.COM/ghost/ and create your admin user. If this process appears to hang then after about 15-20 seconds click the button again and you should be directed to a Ghost login page.
-
Create a cron job to keep the Ghost instance running:
M=$((RANDOM % 10)) (crontab -l 2>/dev/null; echo "0$M,1$M,2$M,3$M,4$M,5$M * * * * ~/apps/GHOSTAPP/start > /dev/null 2>&1") | crontab -
-
Make a note of the following commands which you will use to start and stop the Ghost instance when needed:
- Start:
~/apps/GHOSTAPP/start
- Stop:
~/apps/GHOSTAPP/stop
- Start:
At this point the setup is complete and you can go to https://YOURDOMAIN.COM/ghost/ to make whatever site-specific configuration changes you want.
To run the ghost
command for updates and other maintenance tasks, first set your shell path by running the following command:
export PATH=$HOME/apps/GHOSTAPP/node/bin:$PATH
You'll then be able to run ghost
for updates etc, for example:
export PATH=$HOME/apps/GHOSTAPP/node/bin:$PATH
cd $HOME/apps/GHOSTAPP/ghost
scl enable devtoolset-11 bash
ghost backup --no-setup-linux-user
ghost update --no-setup-linux-user
If you ever need to update Node (like if you are upgrading to Ghost 5.71.0+ since they dropped support for Node v16), you can do that like this. Note that this process can take a LONG time to complete. So set a couple hours aside to do this, and always confirm it completes without any errors.
cd ~/apps/GHOSTAPP
mv node node.old
mkdir node
wget https://nodejs.org/dist/v18.12.1/node-v18.12.1.tar.gz
tar zxf node-v18.12.1.tar.gz
cd node-v18.12.1
scl enable devtoolset-11 -- ./configure --prefix=$HOME/apps/GHOSTAPP/node
scl enable devtoolset-11 -- make -j2
scl enable devtoolset-11 -- make install
# Test that the binary is the appropriate version post install
export PATH=$HOME/apps/GHOSTAPP/node/bin:$PATH
node --version
Then be sure to rebuild the binary dependencies for the new version right after you do that. Just replace 5.XX.X with your current ghost version.
export PATH=$HOME/apps/GHOSTAPP/node/bin:$PATH
cd $HOME/apps/GHOSTAPP/ghost
scl enable devtoolset-11 bash
ghost update 5.XX.X --force --no-setup-linux-user
More ghost commands are documented at: https://ghost.org/docs/ghost-cli/. Note that you must include the --no-setup-linux-user
option when running ghost
commands.