This post describes the steps necessary to install Node.js and Ghost on Windows Server IIS. Ghost is a Node.js web application, specific for just blogging. To run Node.js applications in IIS, you need iisnode as a module. Here is how to install all of this.

Ghost and Node.js on Windows IIS

Ghost is a simple, powerful publishing platform, developed in Node.js. Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

After reading about installing and running node.js applications within IIS on Windows, I wanted to run Ghost on IIS. Tomasz Janczuk developed the iisnode module for IIS to run node.js on IIS, so this should work for Ghost too... Guess we need to install iisnode in IIS then :)

Getting and installing Node.js, iisnode and Ghost on IIS - step 1

First you need all the software:

  1. Grab a copy of node-v0.10.26.x86.msi (node-v0.10.26.x64.msi)
  2. Grab a copy of iisnode-full-iis7-v0.2.7-x86.msi (iisnode-full-iis7-v0.2.7-x64.msi - choose the right flavor for your architecture)
  3. Grab a copy of ghost-0.4.1.zip from https://ghost.org
  4. Install the software on your IIS web server and verify iisnode is registered as a module.
  5. Unzip ghost-0.4.1.zip and place it in your webroot. Ghost cannot be installed in a subdirectory called 'ghost', be aware of that.

Set 2 - Set up and configure iisnode and Ghost Rewrites

Create a web.config file to configure iisnode. It needs to contain a handler for iisnode:

<handlers>
<add name="iisnode" path="index.js" verb="*" modules="iisnode" />
</handlers>

Add a Rewrite directive for Ghost in your web.config:

<rewrite>
<rules>
<rule name="Ghost">
<match url="/*" />
<conditions>
<add input="{PATH_INFO}" pattern=".+\.js\/debug\/?" negate="true" />
</conditions>
<action type="Rewrite" url="index.js" />
</rule>
</rules>
</rewrite>

In the web.config file, you need to configure iisnode too:

<iisnode node_env="%node_env%"
loggingEnabled="true"
debuggingEnabled="true"
devErrorsEnabled="true"
nodeProcessCommandLine=""c:\path\to\node.exe"" />

Depending on your web server security and configuration, you might need to add or tweak NTFS file permissions. The node.exe process needs some read permissions starting from the partition drive (C:, D:, or E: for example).

Set up and configure Ghost - step 3

Important: you need to configure Ghost locally on your development machine.

  1. copy config.example.js to config.js
  2. edit your Production environment:
url: 'http://www.example.com'
host: 'http://www.example.com'
port: process.env.PORT // this one is important!
  1. edit core\index.js for production:
// process.env.NODE_ENV = process.env.NODE_ENV || 'development';
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
  1. make sure files and folders are writeable
  2. install necessary node.js modules for production, run from within your Ghost directory: npm install --production
  3. dry run, run the following command from within your Ghost directory: node.exe index.js , and fix any errors that may occure.

Now everything should run fine when you upload Ghost to your webroot. The logfiles iisnode creates are placed in $webroot/iisnode/*.txt

Ghost logo
Ghost logo

Troubleshooting Node.js-, iisnode- and Ghost errors

Incorrect configured file permissions: How to fix Application has thrown an uncaught exception and is terminated:

As stated earlier in this article, the nodejs.exe process requires read permissions starting from the partition drive for the IUSR user, or group under which the web site and application runs. The special permission needed are Traverse folder / execute file, List folder / read data, Read attributes. This may be a security concern. If file permissions are set up wrong, you can expect the following exception:

Application has thrown an uncaught exception and is terminated:
Error: EPERM, operation not permitted 'd:\'
at Object.fs.lstatSync (fs.js:679:18)
at start (fs.js:1240:10)
at Object.realpathSync (fs.js:1228:3)
at tryFile (module.js:142:15)
at Function.Module._findPath (module.js:181:18)
at Function.Module._resolveFilename (module.js:336:25)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (C:\Program Files (x86)\iisnode\interceptor.js:210:1)

Correcting the file permissions resolves this error.

Node.js Hello World example

A small hello world node.js application comes in handy to debug node.js or iisnode configuration issues. Here's one:

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello, world!');
}).listen(process.env.PORT);

console.log("Node.js running!");

Important to note is the .listen(process.env.PORT) line: Using iisnode, you cannot let your node.js app listen to other TCP-ports than the default HTTP and HTTPS ports; 80 and 443.

Errors during WordPress JSON import into Ghost

WordPress export to Ghost: errors will happen.

Are you trying to import WordPress content into Ghost and that's giving you a hard time? See my post how to import posts from WordPress in Ghost for a solution to the HTTP 500 Internal Server error:

A problem was encountered while importing new content to your blog. Error: unknown

One thing you must not forget is that IIS acts as a reverse proxy, utilizing the iisnode module, for Node.js web applications like Ghost. You may find out the "hard" way, for example when you get too many redirect errors after setting up an SSL certificate.

Donate a cup of coffee
Donate a cup of coffee

Thank you very much! <3 ❤️

4 Comments

  1. Can you guide me on how to serve static files for this setup please? I’ve been googling around for quite awhile but none of the solutions is working for me.

    Thanks.

  2. Nathan

    There’s a typo in the file for \core\index.js

    process.env.NODEENV = process.env.NODEENV || ‘production’;

    should be

    process.env.NODE_ENV = process.env.NODE_ENV || ‘production’;

    • Hi Nathan,

      Thank you very much for your comment and pointing out the typo! I’ve fixed it in the post now.

Comments are closed