Deploy Node.js application using Passenger on Dreamhost or other hosting providers

By: (plus.google.com) +David Herron; Date: Sun Jan 07 2018 16:00:00 GMT-0800 (Pacific Standard Time)

Tags: Node.js

On regular web hosting PHP rules the world, and you might think Node.js has no chance. I haven't cross-checked among current web hosting providers, but Dreamhost does offer the option of hosting Ruby or Node.js applications on a VPS using Passenger. In case you've never heard of Passenger, it is an "app server" for hosting Ruby, Python or Node.js applications. Dreamhost supports all three, but for our purpose we're interested in using this to run a Node.js app.

The process is fairly simple, but Dreamhost somehow managed to make their documentation confusing enough to warrant a blog post. See links below for official documentation.

These instructions are for Dreamhost. It is possible other hosting providers offer a similar capability. Do not try to set up Node.js anything on Dreamhost shared hosting. According to their Wiki, Dreamhosts' security scanning scripts will detect the attempt to compile Node.js and lock your account. Don't do that.

These instructions are for a Dreamhost VPS. The kind which is managed by the Dreamhost control panel. If instead you have a Dreamhost Cloud server, that's a regular plain vanilla operating system installation on which you use normal mechanisms to host Node.js (or other) code.

After logging into the Dreamhost control panel, go to Manage Domains and click on the Add Hosting to a Domain / Subdomain button. This is presuming you'll be adding a new domain, or subdomain, and using that domain to set up Node.js support. Perhaps instead you're interested in adding Node.js to an existing domain in which case you'll be following a slightly different process.

You'll see this screen - which should be familiar if you've done much web hosting on Dreamhost.

Configuration settings to use Node.js on Dreamhost

The annotations in the image are fairly straight-forward. But let's walk through it step by step.

First - select the username under which the application will be hosted. Remember that Dreamhost associates domains to usernames, and the username is then associated with a host on which the domain(s) are hosted. Some usernames are associated with VPS's, and as said above Node.js app's must be hosted on a VPS.

Then go down to the Passenger choice, and enable that button. A pair of buttons will show up letting you select either Ruby or Node.js support. Select Node.js.

A question will pop up saying the web directory must end in /public. Click the OK button to that. We'll discuss it in a minute.

That's the control panel configuration finished - you can do whatever you wish about PHP support. Click the button to go ahead and create the domain. The happy Dreamhost robot thingy will tell you it'll take 5-10 minutes to get that set up, and some amount of other time for the domain name to propagate. We have a couple things for you to do while you're waiting.

Node.js version The default version of Node.js is very old:

$ /usr/bin/node  --version
v0.12.0

It's easy (and recommended) to install nvm to manage Node.js installation, see: (github.com) https://github.com/creationix/nvm Once installed using an up-to-date release is this easy:

$ nvm install 8
v8.9.4 is already installed.
Now using node v8.9.4 (npm v5.6.0)
$ which node
/home/USERNAME/.nvm/versions/node/v8.9.4/bin/node

But the next step is to ensure Passenger knows to use this Node.js release. That's done in the .htaccess as so:

$ cat /home/USERNAME/DOMAIN.COM/public/.htaccess 
PassengerNodejs /home/USERNAME/.nvm/versions/node/v8.9.4/bin/node

We now need to explain a bit about the public directory which was appended to the web directory. It's this directory which Apache will see as the webroot for the domain name. Any request for http://DOMAIN.COM/example.html (or anything else) will be interpreted relative to this directory. To prove it create /home/USERNAME/DOMAIN.COM/public/phpinfo.php containing the following:

<? phpinfo(); ?>

Then visit http://DOMAIN.COM/phpinfo.php and see that it works. Ergo, the public directory is the webroot for the domain. That makes the .htaccess file a normal Apache thing which you can use to do normal Apache .htaccess things.

What's up with this? We proved you can install PHP, and this is supposed to be about Node.js. Patience my friend... We needed to show you the public directory, and how to use it.

The next thing is to install a Node.js application. Let's just borrow one from the Node.js website:

$ cat /home/USERNAME/DOMAIN.COM/app.js 
var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(80);

The Passenger runtime will look for app.js as the entry point for the application. Write your code accordingly. This app is listing on port #80 so it will be accessible as: `http://DOMAIN.COM/' on the bare domain.

This is as far as I've tested.

If you're using Express let's consider how to mount static assets. Clearly from the phpinfo.php example files in the public directory will be automatically handled by Apache. That's a good thing because Apache will serve those files much more quickly than the Node.js app, and it removes overhead from the Node.js code.

Therefore, this line of code is unneeded:

// app.use(express.static(path.join(__dirname, 'public')));

Another typical thing is to host Bootstrap, jQuery and other front-end assets as Node.js modules in the node_modules directory. For that purpose the following statements are used in an Express app.js:

app.use('/assets/vendor/bootstrap', express.static( 
  path.join(__dirname, 'node_modules', 'bootstrap', 'dist'))); 
app.use('/assets/vendor/jquery', express.static( 
  path.join(__dirname, 'node_modules', 'jquery'))); 
app.use('/assets/vendor/popper.js', express.static( 
  path.join(__dirname, 'node_modules', 'popper.js', 'dist')));  
app.use('/assets/vendor/feather-icons', express.static( 
  path.join(__dirname, 'node_modules', 'feather-icons', 'dist'))); 

While this will work, it means Express is responsible for serving those files. As we just said, it'll be less overhead to let Apache serve those files. You might want to spend the time to work out how to deploy those files into the public directory so Apache can do so.

It's your choice whether to serve static files through Express or via Apache. There are legitimate reasons to do it either way. It boils down this way:

  1. Any file that must be delivered unmodifed should be (but doesn't have to be) delivered directly by Apache.
  2. Any file that must be modified must be delivered by Node.js code.

How to deploy? The script we showed earlier is simple enough to edit on the server. But the typical thing will require something more. As I've not gone through that let me suggest the following untested idea:

  1. Use git to deploy code on a server ...
    • e.g. npm install USERNAME/PROJECT-NAME to deploy from a github repository, or else npm install http://GIT-HOST.COM/PATH/TO/TARBALL.tar to deploy from a tarball.
    • In my case, I have a GOGS server (github clone) for personal repositories. I can test the code on my laptop, and push to the GOGS server. That server exports tarball URL's that can be used as shown here.
  2. On the server, use npm install (as just shown) to install all dependencies
  3. Work out how to separate the static assets which should be delivered with Apache into the public directory

Links: