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).
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
corepackage groups installed)
- You have SELinux turned on
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://
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.
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:
[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.
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 --add-port=2368/tcp --permanent firewall-cmd --reload
Once everything is setup, you should be able to run
systemctl start [email protected]
and be up and running. Point your browser
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 "############"