Running Hapi with Passenger on Heroku

Published on 05 November, 2015

Before it changed it’s name to Passenger, the application server was called mod_rails and used as module within Apache HTTPD to get your RoR applications up and running.

What Passenger does is tuning itself automagically to get the most out of your server’s hardware. Like using all available CPU cores by default, HTTP caching without any configuration and load-balancing across the application’s processes. If you have ever ran Node.js in production, you’ll know how hard it is to tweak everything correctly according to the server it’s running on. So a little bit of help in this is always welcome.

Running Node.js with Passenger

The first step you’ll have to take is to actually install Passenger into your environment. They support many (unix-based) platforms and you can read all about it on their excellent guides. For now let’s assume that you’ll want to run Passenger on OSX and you can install it very easily through Homebrew;

Afterwards, check if everything has been installed correctly by running another command in your terminal;

$ sudo passenger-config validate-install

You’ll see if all checks are passed and otherwise some suggestions to fix any of them. If that’s the case, please refer to their OSX specific guide.

Now that you are all set, let’s start a Node.js script from the command-line. We’ll use their own Node.js demo app for demonstrative purposses. By running the following command in the directory of the app, you’ll start Passenger in the foreground;

$ passenger start --app-type node --startup-file myapp.js

With this command you tell Passenger to run a Node.js application ( — app-type node) and you’re application is living in the file myapp.js ( — startup-file myapp.js).

Configuring Passenger for Hapi.js

As you might have noticed is that we’re now running the application with Node’s built-in HTTP module. To make Passenger compatible with a Hapi.js applications, we slightly need to change the way Hapi listens to incoming requests. Instead of running on a port number, it needs to listen to an unix domain socket like described in the Passenger’s guide;

if (typeof(PhusionPassenger) !== 'undefined') {
  PhusionPassenger.configure({ autoInstall: false });
}

var Hapi = require('hapi');
var server;

if (typeof(PhusionPassenger) !== 'undefined') {
  // Requires Passenger >= 4.0.52!
  server = new Hapi.Server('/passenger');
} else {
  server = new Hapi.Server('localhost', 3000);
}

server.route({
  method: 'GET',
  path: '/hello',
  handler: function (request, reply) {
    reply('hello world');
  }
});

server.start();

Running Hapi.js with Passenger on Heroku

Now that we’ve setup a simple Hello World example locally. Let’s get it running on the Heroku platform.

As you might know, your processes on Heroku can be managed very easily with Foreman and so called Procfiles. A typical Procfile for a Node.js based Passenger application may look like this;

web: bundle exec passenger start --app-type node --startup-file myapp.js --port $PORT

Have you noticed the $PORT environment variable? This is a variable exposed by Heroku to bind your application to their HTTP routing layer.

Another thing you might have noticed is the usage of bundle exec. This is a very known command to the Ruby world for running your Ruby application with any installed third-party gems. Aren’t we running a Node.js application? Yes we absolutely do and Heroku has some built-in mechanism to detect which buildpack to use for your application. Luckily Heroku gives us a way for running your application with multiple buildpacks. So you’ll have to create a .buildpacks file with the following contents;

https://github.com/heroku/heroku-buildpack-nodejs.git https://github.com/heroku/heroku-buildpack-ruby.git

And a Gemfile where we tell the Ruby buildpack to install Passenger;

source 'https://rubygems.org' gem 'passenger'

Now you’re all set to run Passenger on Heroku!

Like to discuss or having a question? I am @robindvleuten Twitter.