Ghost, Nginx, Passenger & FreeBSD

Page content

I wanted a place to document things I do. It seems like it would be useful to refer back to them for redoing a setup, and maybe improving on a setup. I figured the first post should be one related to how I got all of this running.

So I started looking at systems for making this easier. A blog seems like a system that would help with that, and I started looking around. Well, that and a friend insisting that I help find him something do be a content management system, but that’s a different topic, which will inevitably yield a different result.

Ghost - A solution?

Ghost seemed like a good solution to the blog problem. I’m not a big fan of JS, but it’s got quite a bit of attention, node.js is a pretty interesting solution, and well, it’s not php.

The Demands.

There’s just the last hurdle - getting it installed into an environment that meets my demands:

  • FreeBSD - I’ve been a FreeBSD user since 2.2.6 days. I’m not looking to change now.
  • Nginx - Been using this for a few years now, love it’s syntax, love it’s performance.
  • Must not use per-site application servers, as that just gets to be a maintenance nightmare.

The Solution.

The 3rd demand above is solved by Phusion Passenger. I’d used Phusion Passenger a couple versions ago, back when it only supported Ruby. To find out that it supports Node.js and Python as well has been sort of a surprise for me, and given that I now have a demand for a node.js application, very benefitial!

So, my solution is to run FreeBSD 10.2 under the hood. Put Nginx on top of that, with the Passenger module loaded, so that it can serve up the Node.js application that is Ghost.

A reference to start from

I’d run across someone who was interesting using Passenger + Nginx, but it seemed to be an old version of Passenger (and definitely an old version of Nginx). At the time of this writing, we’re on Nginx 1.8.1, and Passenger 5.0.26. Plus, as they noted, Passenger has been updated to no longer need the extra directory structure & JS file they created.

For reference, their post was here: https://langrisa.eu/setting-up-ghost-blog-on-nginx-phusion-passenger/

Installing our needed software:

I leave installing FreeBSD 10.2 to the pros: http://freebsd.org - Read the handbook if you ever get stuck. It’s amazing. There were a bunch of packages that we’ll need for ghost, so let’s get those installed first:

pkg install node012 npm012 sqlite3 gmake

I’m going to be using sqlite3 later for Ghost, so there may be a dependency there you don’t need if you’re looking to use a different database. gmake is needed by npm later on, but isn’t part of the dependency tree for node012 or npm012. A word of warning, look at what version of node/npm is supported by Ghost before trying to say, install Node.js 5. Ghost (at time of writing) didn’t support Node.js 5.7.

Now, Nginx + Passenger is a little funky. First off, there’s no pre-packaged Nginx version that includes the passenger module, so you’ll need to build it from sources - specifically, the ports tree. If you don’t have it already, grab it (as root):

portsnap fetch extract

If you already have the ports collection, just do portsnap fetch update to get the latest updates.

So then we need to compile Nginx with Passenger support. (I’d normally do this with portmaster, as I haven’t gotten to using the newer utilities, like poudriere or synth, but ‘make’ will do for this.)

cd /usr/ports/www/nginx
make install

During the blue configuration screen, make sure to scroll down to the 3rd-party contributed modules, and select Passenger. Let make do its thing, and it’ll tell you when it’s all done.

We’ll do the same with the passenger port, as this installs all the passenger components:

cd /usr/ports/www/rubygem-passenger
make install

Similarly, select Nginx from the web-server selection on the bottom. I’m not sure why the passenger port spits out lines about Apache configurations when we’re using Nginx, but it does.

Configuring the environment:

Passenger doesn’t need any configuration specific to itself, but it does need a bunch of entries in the nginx configuration files, and needs a couple lines in nginx.conf itself.

You should double-check your passenger folder using passenger’s configuration tool. We’ll need to know this for the nginx config: passenger-config --root

Add the passenger_root & passenger_nodejs lines to nginx.conf, inside the http{} part of the config. This lets nginx’s passenger module know where to find passenger’s programs (Agent, Watchdog, etc) as well as where to find node.js’s executable.

http{
  # other configuration information will exist here.
  # ADD these two lines for Passenger:
  passenger_root   /usr/local/lib/ruby/gems/2.1/gems/passenger-5.0.26;
  passenger_nodejs /usr/local/bin/node;
  # ...
  # ... more configuration here...
  # ...

  # optional: include at the bottom of the http block to load
  # additional files, useful for per-site config files:
  include /usr/local/etc/nginx/sites/*.conf;
}

I like using the include line at the end of that block to create a single file for my server{} blocks for each domain, eg: /usr/local/etc/nginx/sites/binarydragon.com.conf

It means that my entire server config (like the one below) goes into a single file, and if I wanted a second domain/subdomain, I could simply create another file.

Then, we need a server{} config for the domain we’re setting up for ghost.

server{
  listen 80;
  server_name binarydragon.com www.binarydragon.com;
  passenger_enabled       on;
  root /usr/home/kovus/ghost.binarydragon.com/core/shared;
  passenger_app_root      /usr/home/kovus/ghost.binarydragon.com;
  passenger_app_type      node;
  passenger_startup_file  index.js;
  passenger_restart_dir   /usr/home/kovus/ghost.binarydragon.com;
}

A few notes:

  • In a little bit, I’m going to install ghost into the folder /usr/home/kovus/ghost.binarydragon.com
  • The minimum parameters for a normal node.js passenger app are passenger_enabled, root, and passenger_app_type. The other parameters are needed because Ghost isn’t a passenger-friendly node.js application.
  • I’m not actually sure if I have the ‘root’ folder specified correctly to minimize the amount of strain that Nginx could relieve from Passenger. I’ve read up that the themes include additional content that could be served by nginx, but it seems that it’s location on the fileserver and URI don’t match nicely to let Nginx serve them statically.
  • The passenger_restart_dir is sort of optional. I’m used to being able to touch tmp/restart.txt inside a passenger app folder to cause Passenger to reload the app. With this parameter, it would just be touch ~/ghost.binarydragon.com/restart.txt (note the lack of ’tmp’).

Installing Ghost

Onto the next part, since we have our configuration all setup for Ghost, now we need Ghost.

cd ~kovus
fetch --no-verify-peer https://ghost.org/zip/ghost-latest.zip
unzip -d ghost.binarydragon.com ghost-latest.zip

I’m pretty simple, so I didn’t need much in terms of big databases, so sqlite3 was enough for me. Sqlite3 is apparently in an unexpected location, so we’ll need support for that to be compiled before we can run npm install.

cd ghost.binarydragon.com
npm install sqlite3 --sqlite=/usr/local
npm install --production

Follow that up with some of the basics from the Ghost getting started:

cp config.example.js config.js
edit config.js

Ok, use whatever editor you like. I’ve been using ee for a long time for simple text editing. (And vi when I need to do more complex stuff.)

Lastly, don’t forget to startup nginx (as root):

sysrc nginx_enable=YES
service nginx start

Pull up your favorite web-browser to your site, and away you go.