Ghost blog on CentOS 7

It seems only appropriate that my first post is about how I setup the blog. Ghost is Node.js based blogging software. Having setup both WordPress and Drupal before, I definitely prefer it for basic blogging/website needs. More information can be found at their website https://ghost.org/. We'll be running the Node.js vm behind an Apache reverse proxy with a separate MySQL/Mariadb server but we'll only cover how to setup the Node.js portion of this. The internet is abound with tutorials for both Mariadb as well as Apache/Nginx reverse proxy tutorials. I'll eventually put one up, but for now, just use ones already available. Lets get started.

If you don't want the labor pains, just the baby; go to the bottom for the TLDRScript™ section. This is a script you can copy and paste and get everything we do here done, automatically (could be useful for a kickstart %post section).

Assumptions

To make this post relevant, we need to make some assumptions.

  • You have root access on a server with internet access
  • You're running CentOS/RHEL 7.x (this was done on 7.2.1511 with minimal and core package groups installed)
  • You have SELinux turned on

Installing Node.js

First we need to install the Node.js application server. I followed the directions almost verbatim from their github page. For the entirety of this tutorial, I'm assuming you're executing commands as root (or via sudo)

#Their suggested 
#curl --silent --location https://rpm.nodesource.com/setup | bash -
cd ~
wget https://rpm.nodesource.com/setup -O setupNodeRepo.sh
chmod +x setupNodeRepo.sh
./setupNodeRepo.sh
yum -y install nodejs

See the commented out line? That is what Node.js recommends you do to get their bash setup script and run it. Doing that is actually a little dangerous. Its literally taking the output of curl (which pulls the text of the bash script down) and sending it straight into your bash interpreter. Whenever possible, you should inspect any code that you will run on your system. Just wget the script, use vim/less/nano/yourfavoriteeditor to inspect it before you just go running it. After that, we use yum to install Node.js.

Preparing your environment

We're going to be using a template/instance model with our Systemd service (more on that later). As such, lets make /opt/node/myghostblag:

mkdir -p /opt/node/myghostblag

From here, lets download the latest ghost release, extract it, and set it up.

yum -y install unzip
wget https://ghost.org/zip/ghost-latest.zip
unzip -uo ghost-latest.zip -d /opt/node/myghostblag
cd /opt/node/myghostblag
npm install --production

Assuming there are no errors, lets open up the system firewall and test it out.

firewall-cmd --add-port=2368/tcp --permanent
firewall-cmd --reload
npm start --production

Point your browser to http://:2368 and you should be greeted with your newly created blog. Take this opportunity to setup your admin interface by going to http://:2368/ghost and run through the setup
Important note: if the system running your blog is not on your local network, save setting up your admin interface until later. You don't want to send the admin credentials in clear text over the internet!

Once you hit the home page, switch back to your ssh window and shutdown the Node.js instance with ctrl+c.

Systemd service

With CentOS/RHEL 7, we get Systemd replacing the old SysVInit system. This means a new unit file (replacement for the old Init Scripts) leveraging new capabilities. In this case, we're going to utilize the templates/instances feature of Systemd unit files.

For the purposes of this discussion, interpret unit to mean service. This is not true in all cases. There are many different types of units. For more information, the gents over at Digital Ocean wrote up a very nice tutorial. On to our template unit file:

/etc/systemd/system/[email protected]

[Unit]
Description=NodeJS application - %i
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/node/%i
User=nodejs
Group=nodejs
ExecStart=/usr/bin/npm start --production
ExecStop=/usr/bin/npm stop --production
Restart=always
SyslogIdentifier=NodeJS-%i

[Install]
WantedBy=multi-user.target

We won't go into detail of every portion of the unit file, but the important thing to note is the %i. When making an instance, you copy or sym-link from the template to the specific instance.

ln -sf /etc/systemd/system/nodejs\@.service /etc/systemd/system/nodejs\@myghostblag.service systemctl daemon-reload

This will create a new unit called [email protected]. Systemd will dynamically interpret everything after the @ as the instance name, and subsequently replace it in the unit file where %i appears. You need to run systemctl daemon-reload every time you make a manual change to the unit files so that Systemd notices the change.

At this point, you should be able to run systemctl daemon-reload (to have systemd re-parse its unit files) and systemctl start [email protected]. Your blog should start. Check it with systemctl status [email protected].

Edit: So, we all learn new things. I learned that you don't actually need to create the symlink, simply starting an instance service causes it to get created. How neat is that!?

Permissions, SELinux, and Firewalls.

Make sure that your node user owns the /opt/node directory and everything under it:

chown node:node -R /opt/node

I don't actually do any specific SELinux configuration. As such, Node.js is running unconfined.

As for the firewall, just poke a hole using firewall-cmd:

firewall-cmd --add-port=2368/tcp --permanent
firewall-cmd --reload

Summary

Once everything is setup, you should be able to run

systemctl start [email protected]

and be up and running. Point your browser

TLDRScript™

See below for the script you can just run and get the same setup, automatically. Make sure you're running this script as root

#!/bin/bash

#Switch to roots home directory, get the Node.js repo script, and run it
cd ~  
wget https://rpm.nodesource.com/setup -O setupNodeRepo.sh  
chmod +x setupNodeRepo.sh  
./setupNodeRepo.sh

#install nodejs and unzip
yum -y install nodejs unzip

#setup ghost blog
mkdir -p /opt/node/myghostblag  
wget https://ghost.org/zip/ghost-latest.zip  
unzip -uo ghost-latest.zip -d /opt/node/myghostblag  
cd /opt/node/myghostblag  
npm install --production

#open up firewall
firewall-cmd --add-port=2368/tcp --permanent  
firewall-cmd --reload

#add systemd template unit file
cat <<EOF > /etc/systemd/system/[email protected]  
[Unit]
Description=NodeJS application - %i  
After=network.target

[Service]
Type=simple  
WorkingDirectory=/opt/node/%i  
User=nodejs  
Group=nodejs  
ExecStart=/usr/bin/npm start --production  
ExecStop=/usr/bin/npm stop --production  
Restart=always  
SyslogIdentifier=NodeJS-%i

[Install]
WantedBy=multi-user.target  
EOF

systemctl daemon-reload

#set ownership
chown node:node -R /opt/node

#start, enable and check status
systemctl start [email protected]  
systemctl enable [email protected]  
systemctl status [email protected]  
echo "############"  
echo "Script completed!"  
echo "############"