Robin van der Vleuten

Running Hapi with Passenger on Heroku

Before it changed its name to Passenger, the application server was called mod_rails and ran as a module inside Apache HTTPD for Ruby on Rails applications.

Passenger tunes itself to make better use of the server: all available CPU cores by default, HTTP caching without extra configuration, and load balancing across application processes. If you have run Node.js in production, you know how much tuning can depend on the server. A little help is welcome.

Run Node.js with Passenger

The first step is installing Passenger. It supports many Unix-based platforms, and the Passenger guides cover them in detail. For this example, assume you want to run Passenger on macOS and install it with Homebrew.

Afterwards, check whether the install worked:

bash
$ sudo passenger-config validate-install

Passenger tells you whether all checks passed. If something fails, use the macOS-specific guide to fix it.

Now start a Node.js script from the command line. We will use Passenger's Node.js demo app. Run this command inside the app directory to start Passenger in the foreground:

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

This tells Passenger to run a Node.js application with --app-type node and use myapp.js as the startup file with --startup-file myapp.js.

Configure Passenger for Hapi.js

The demo app uses Node's built-in HTTP module. To make Passenger work with a Hapi.js application, Hapi needs to listen a little differently. Instead of listening on a port number, it needs to listen on a Unix domain socket, as described in Passenger's guide:

js
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()

Run Hapi.js with Passenger on Heroku

Now that the Hello World example runs locally, we can run it on Heroku.

Heroku processes can be managed with Foreman and Procfiles. A typical Procfile for a Node.js Passenger application looks like this:

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

The $PORT environment variable comes from Heroku. Your app must bind to it so Heroku can route HTTP traffic to the process.

You may also notice bundle exec. That is a Ruby command for running an application with the gems installed by Bundler.

That sounds odd for a Node.js application, but Passenger is installed through the Ruby buildpack. Heroku detects the Node.js buildpack automatically, and it also supports multiple buildpacks. Create a .buildpacks file with this content:

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

Then add a Gemfile so the Ruby buildpack installs Passenger:

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

Now Passenger can run the Hapi application on Heroku.