/ node.js

Blogging from the ground up: part 2

This article covers the Node.js side of things. We'll be creating a new Linux user for node to run under, installing Ghost, and finally running Ghost using PM2.

Getting the sysadmin stuff out of the way


As a disclaimer, I'm not going to go over everything under the sun about DevOps best practices. In fact, I'm really only going to be talking about running Node under its own user. Please let me know if there's anything you actually disagree with or think should be done better! There are a couple more suggestions in the Additional notes section.

Creating the node user

In order to be able to manage and secure permissions a little bit better, let's create a new user node to run our Node.js applications under.

$ sudo groupadd www
$ sudo groupadd -g 600 node
$ sudo useradd -md /home/node -u 666 -g node -G www -s /bin/bash node
$ id node
uid=666(node) gid=600(node) groups=600(node),601(www)

While we're at it, let's also change the ownership of the /www mount point:

$ sudo chown -R node:www /www

Installing Ghost

Ghost logo

Originally, I was going to write about my own specific deployment process/workflow, but I think it's out of the scope of this post. For the sake of brevity, I'll be going through the Ghost installation process with the following assumptions:

  • Ghost is going to be manually installed using the latest release.
  • Upgrades will be performed manually.
  • Changes to code, themes, etc. will be done on the server directly, with the understanding that it shouldn't be done this way.

The steps in this section will be performed as the node user; so let's switch now:

$ sudo su node

There are more comprehensive documents on the Ghost website.

Getting the Ghost release

We'll wget the current Ghost release and place it in the /www/blog directory:

$ cd # this brings us to /home/node
$ wget https://ghost.org/zip/ghost-0.3.3.zip
$ unzip ghost-0.3.3.zip -d /www/blog

Installing dependencies

The last step to installing Ghost is to just install the NPM dependencies. We can also give it a test run.

$ cd /www/blog
$ npm install --production
$ npm start

Checking to see that it's reachable:

$ curl -I http://localhost:2368
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 4588
Date: Wed, 08 Jan 2014 07:26:47 GMT
Connection: keep-alive

Configuring Ghost

After starting Ghost for the first time, it will automatically create a sample config.js file. Modify the production property of the config variable in that file to have the settings you require. As far as the "mail" section goes, we won't cover setting up the mailer. For more information on that, you can check the Ghost Mail docs.

Installing and setting up PM2

PM2 logo

Installing pm2

First, we will install pm2 as a global node module. Once it's installed, we will start pm2 up using an init.d script, which will also allow it to start up on boot.

$ # Perform the following as a user who has sudo access, e.g. ec2-user
$ sudo npm install -g pm2
$ sudo pm2 startup centos -u node # we use the centos environment since we use chkconfig
$ sudo mv /etc/init.d/pm2{-init.sh,}
$ sudo chkconfig pm2 on

Managing Ghost with PM2

To make things easier for us in the future, let's create a JSON configuration file for running Ghost with PM2. I'll just place the file in /www/blog/blog-pm2.json:

  "name" : "ghost",
  "script" : "/www/blog/index.js",
  "out_file": "/www/var/log/sublog.out",
  "error_file": "/www/var/log/sublog.err",
  "pid_file": "/www/var/run/sublog.pid",
  "instances": "max",

  "NODE_ENV": "production"

Finally, we can start Ghost for real!

$ mkdir -p /www/var/log /www/var/run
$ cd /www/blog # Ghost image uploads are broken if you don't start from this directory
$ pm2 start blog-pm2.json

You'll notice that I also explicitly specified log and pid file locations. If you don't do so, then PM2 will place them in ~/.pm2/{logs,pid} by default.

Additional notes

Logging in for the first time

Great, we've gone through all this work, and we haven't even opened up Ghost in the browser! Sorry to disappoint, but making Ghost publicly accessible will be covered in the next article. However, if you're impatient, then here's what you can do. Go to your AWS security group preferences and open up port 2368 (or whichever port you're running on). Then you can go to http://yourdomain.com:2368/ghost to login.

sudo me baby

During setup, you may be switching to the node user, and it may be convenient to be able to use sudo for things like starting/stopping pm2, etc. Do a visudo to edit the /etc/sudoers file, and add the following line:

node    ALL=/sbin/service pm2 *, /sbin/service nginx status

Take it to the limit

By default the AMI I have set up has a file descriptor limit per user of 1024. This limit is viewable using the command ulimit. Once your web application starts to get more traffic, you'll want more room to work with. So let's go ahead and change that limit.

Edit/append the following line to

fs.file-max = 70000 # a bit arbitrary

Then in /etc/security/limits.conf add the following lines:

nginx           soft    nofile         5000
nginx           hard    nofile         15000
node            soft    nofile         5000
node            hard    nofile         15000

Finally, reload the sysctl configuration using sudo sysctl -p.


So by now, we have Ghost running with pm2 daemonizing it. Next time, we'll cover using Nginx to reverse-proxy Ghost and to secure Ghost administration using SSL.