Scott Hanselman

How to install the nodejs Ghost blogging software on Azure Websites

October 24, 2013 Comment on this post [26] Posted in Azure | nodejs | Open Source
Sponsored By
The Ghost Admin running in Azure

UPDATE - These instructions are out of date!

GO HERE for a one-click installation of Ghost to Azure technique.

Like many folks, I watched the recent Kickstarter for the nodejs-based Ghost Blogging Platform with great interest. There are lots of folks, myself included, who believe that WordPress has jumped from blog to complete and complex CMS, and there's value in a simple platform for "just blogging."

SIDE NOTE: Every time a post something I get at least 4 emails from well-meaning readers about how my spelling and grammar sucks. And I appreciate you all. For a few posts, I'm trying an online service called Grammarly that promises to be 10x better than word processors in proofreading. 10x is like 9 x's more than 1 x and it's clearly a much more bigger number. We shall be seeing how it do. ;)
(Of course, Grammarly HATED this paragraph.)

Ghost is very minimal, evocative of Medium in style and essence. Ghost is basic and clean publishing, plus it's open source and being very actively developed on GitHub. As of the time of this writing, it's version 0.3.3, and I will do my best to keep this blog post up to date. Things are moving fast.

It'd be nice to get Ghost running on Azure, and since it's a reasonably complex node application, it's good exercise for everyone. Jeremiah Billmann and a number of others have been working on some of the small compatibility issues (like image uploads and pathing), and soon we'll be able to get Ghost up and running in just a few commands. Jeremiah has a great writeup here. He and I spent part of the afternoon Skyping and pairing and trying alternate ways to get Ghost running. My instructions are different and not meant to take away from his. There's more than one way to deploy a site.

I'll point out workarounds, and again, remove them (strike them out) as Ghost takes PRs. (BTW, the Ghost Team is SO NICE. So nice.)

Installing Ghost on Azure Web Sites

I'm using Windows, but these commands should also work on Macs.

IMPORTANT CHOICE: You can just go download Ghost at and everything comes pre-built.
If you download it, you can SKIP getting Grunt, Ruby, Sass, below. Just get node, and move to the Configuring Ghost before you Deploy section further down!
Otherwise, if you "git clone" like I did, you're doing stuff manually, certainly more than is needed, but perhaps learning more about node.
So, want it prebuilt and ready to go? Download Ghost.
Learning, hacking? git clone


To start, you'll want these things locally:

  • nodejs - Get 0.10 or above. Install from
    • Then get the node Azure command line
      • npm install azure-cli --g
  • Git - Get it from
  • Get Grunt
    • npm install grunt-cli --g
  • ruby - Get 2.0 or above. Install from
  • Bourbon and Sass. Bourbon brings in Sass.
    • gem install bourbon
  • Get an Azure Trial if you don't have one.
    • You get 10 websites free and can run whatever you want, node, php, ASP.NET, etc.

Setup the Azure Command Line

Connect the Azure CLI (command line interface) to your account like this:

  • azure account download
    • launches browser, sign in, get cert
  • azure account  import
  • [optional] azure account set "account name"

Getting Ghost

You can either download Ghost prebuilt or get the source and build it. Here's both ways. The Source is harder, but I learned some things.

Getting Ghost from Download

Download Ghost and unzip it into a folder.

> azure site create [Site Name] --location "West US" --git
# note that this line setups the "git remote add..." and you can confirm with "git remote show"
> azure site config add NODE_ENV=production
# sets an env var for node, up in azure
> npm install
# get all the modules locally, only needed if you are running locally
> copy con server.js
var GhostServer = require('./index');
# Ghost uses index.js, not server.js. Make a server.js.

Now, skip over this to the Configuring section. The --git switch above set up your remote git repo in Azure.

Getting Ghost from Source

Now, run these commands from the command line. Read carefully, and think. You can change Site Name, Location. You can confirm the git remote. Things with # are my comments, don't type them. Note the copy con. Don't like it? Use a text editor.

> git clone
> azure site create [Site Name] --location "West US" --git
# note that this line setups the "git remote add..." and you can confirm with "git remote show"
> azure site config add NODE_ENV=production
# sets an env var for node, up in azure
> npm install
# get all the modules locally
> grunt init
> grunt prod
#preps your CSS and JS
> copy con server.js
var GhostServer = require('./index');
# Ghost uses index.js, not server.js. Make a server.js.

At this point, you have a site running in Azure with nothing in it.

You currently have a local Git repo with a Git Remote pointing to Azure.

Configuring Ghost before you Deploy

Your cloned Ghost includes a config.example.js. I renamed it to config.js and made a few changes.

production: {
url: '',
mail: {
transport: 'SMTP',
options: {
auth: {
user: 'poop',
pass: 'alsopoop'
database: {
client: 'sqlite3',
connection: {
filename: path.join(__dirname, '/content/data/ghost.db')
debug: false
server: {
host: '',
port: process.env.PORT

Go to the production section, check your URL, your user/pass for mail (mine is invalid, I'll do it later), and check the port: section. Make it "process.env.PORT" which is what Azure uses to get the Port number.

Now open your .gitignore, comment out these lines. enabling you to check in these files and directories.

# /core/client/assets/css
# /core/built
# /core/client/assets/fonts
# config.js

When you ran grunt prod earlier, it built files into /core/built, that's why we need that checked in.

NOTE: Of course, we are using Git for deployment. If you don't want to use Git, you can just FTP the files into Azure.


Now, add, commit, push.

> git add .
> git commit -m "hope this works"
> git push azure master

Azure (specifically the Kudu subsystem) will run npm install, so this deployment may take a while. You can watch it live in the Azure Portal if you like.

There's my deployment in the Azure Portal

You can also look at deployments from the command line:

C:\Ghost>azure site deployment list hanselmanghost
info: Executing command site deployment list
+ Getting deployments
data: Time Commit id Status Author Message
data: ------------------- ---------- ------ --------------- ----------------
data: 2013-10-23 15:59:38 783746f6a1 Active Scott Hanselman adding server.js
info: site deployment list command OK

OK. It's deployed...except, today...there's a bug.

The Big Hack as of October 23rd...THIS HAS BEEN CLOSED AND MERGED!

Here's the hack/workaround. There's a node module called Express-HBS that is a handlebars template engine that supports partials. It has a caching bug, but there's been a Pull Request opened for express-hbs with the fix. As soon as that fix gets merged in, this whole workaround just goes away.

But, for now, you need to patch the express-hbs/lib/hbs.js with this version (link to RAW file)

How do you patch this in Azure? You can either FTP it, or use this super-secret Azure Kudu DebugConsole that is public but no one has noticed it yet.

Visit in your browser. The name and password are the same as your Git Deployment name and password.

Kudu DebugConsole

Navigate to site/wwwroot/node_modules/express-hbs/lib by clicking folders. Click the Edit button, which brings you to a live multiline textbox. Copy paste the patched hbs.js into the textbox, and click Save.

Once you've patched hbs.js, you're all good. Go hit and sign in.

My ghost blog works

Where are the images for my posts stored? They are right there on the file system where Ghost put them. Here's me remoted into the Azure WebSite with the Kudu Debug Console. There's the images.

Images in Ghost in the Azure Kudu Debug Console

You might not want to scale this website out to more than once instance, as you'll have file contention, but you could certainly scale it up. Since it does so little, I don't see Ghost having much trouble scaling to the average blog's traffic.

Myself, Jeremiah, and lots of other folks are going to work on getting Ghost up and running even easier. Once that hack is gone, there's about 2 or 3 steps could be removed and this will be really quite streamlined. I've got some ideas about using Custom Azure Websites Deployment Scripts to move the Grunt build steps into Azure. They would happen after the push. We'll see!

Have fun, I am! Also, check out, my new show where I learn Azure from the folks that built it...coming soon!

UPDATE: Felix Rieseberg built an Azure Ghost Updater! Try using it to update your Ghost installations on Azure!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
October 24, 2013 18:02
Technically it is nice to see new applications done in node.js . I as rarely come across any complete opensource node.hs app .

but economically PHP based blog is still the best bet for low cost blog .
October 24, 2013 18:27
Further to my previous comment (which seems to have disappeared now), I had to add the Server.js in the root that Jeremiah mentions in his article.

After that, I had several problems with the dependent modules not being installed properly, so I had to delete the node_modules folder (use FTP for this, not the Debug console, which seems to eat into your CPU time), and then run npm install from the console.
October 24, 2013 19:20
Nice write-up, Scott. I've written up some brief instructions on setting up SMTP with SendGrid for Ghost on Azure here:

Oh, and I used WebMatrix to update hbs.js :)
October 24, 2013 19:24
FYI, I had to change the server.js file to: var GhostServer = require('./index'); // <- note the dot.

Otherwise it complained of not being able to find the required 'index' module.

But i'm still getting errors. After "fixing" the server.js file I now get this errors (even after cleaning out the node_modules folder and recreating it by running npm install in the DebugConsole):

Error: Cannot find module 'readdirp'
at Function.Module._resolveFilename (module.js:338:15)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (\\\volume-23-default\3e8f5903a1e69d75455d\d26622ea3271442a98b294dea03638c8\site\wwwroot\node_modules\express-hbs\lib\hbs.js:4:16)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)

I installed it by downloading the from the website.
October 24, 2013 20:21
Hi Scott, great post. I love seeing you dive in on projects like this and engage with different development communities.

The Grammarly aside was interesting. I have no idea how much it caught, but the post was very solid overall. Because it might be interesting, I did notice a couple things, for comparison purposes.

  • I'm assuming this is humor, left uncorrected on purpose:
    "We shall be seeing how it do. ;)"

  • This one seems like the most likely 'catch' it might have made:
    "My instructions are a different and not meant..."

  • And this is nigh impossible considering the tech jargon it hinges on:
    "You currently have a local Git repro with a Git Remote pointing to Azure."
    (repro -> repo)

I think you do an excellent job, especially as a solo editor. Sure there are little things occasionally, but you don't have a writing staff, so that's perfectly reasonable. So much more important than the grammar fussing? You can write engaging and funny technical content. I'd like to see Grammarly try that!

Wishing you the best in your internet pedant adventures,

October 24, 2013 21:33
Stefan - Log into the Debug Console...what if you run npm install from in there?

Colin - Thanks! Fixed.

Derek - Ya, there's a LOT of modules and they seem to load SLOWLY...
October 24, 2013 21:55
Scott - As mentioned, I tried that. Same result :(
October 24, 2013 22:01
So if you use the prebuilt ghost you have to do a couple of items to get this working.

Details see:

But basically:
Edit package.json
- Update express to version 3.4.0
- Update express-hbs to version 0.3.0
"express": "3.4.0",
"express-hbs": "0.3.0",

- Edit express-hbs/lib/hbs.js (I used the code from jbillmann blog post)
- Edit server.js to var GhostServer = require("./index"); as Stefan and jbillman note
- Profit

Sorry about the formatting, can't seem to get it right in the comment box.
October 24, 2013 22:13

Use the hbs.js that Scott links to in his post.

I made a mistake when I typed in my previous comment.
October 24, 2013 23:28
thanks for taking the time to write this up Scott! The express-hbs patch was the last thing in needed to get up and running. I'm using a similar means of deployment, except i'm using a custom node deployment script and syncing from github. really easy to generate using the azure-cli tools and give that extra bit of flexibility. like keeping the source under the /src directory in your repo, which is all the customization I need for now.

The debug console is pure gold! I have about 20 azure web sites that I manage in production so that will now be a daily use tool for me. This needs to be right on the dashboard of every azure website...already put the suggestion on uservoice :)
October 25, 2013 0:05
Hi Scott
we cant reach windows Azure from Iran even for trial time?
please help me how can I reach to the Azure just for Trial time and check it ?
please help me ?
October 25, 2013 1:30
Neat, I've been intrigued by Ghost, looking forward to giving it a try.
October 25, 2013 15:11
I can't stand grammar nazis. They're usually the meanest people with strong sociopathic tendencies. The fact that they actually write an email to you says a lot about them. Life is too short to be proofreading a blog post that's not your own. Even one as popular as this one. *sigh*
October 25, 2013 21:02
I think you need to change package.json and set express to 3.4.0 and express-hbs to 0.5.0. If I don't do that, I get an error on the signup page.
October 25, 2013 23:28
Scott, Ghost is looking nice. Meanwhile checkout another open source blog system; MiniBlog by Mads Kristensen (MSFT) on GitHub is getting traction. With its light weight and non-database requirement, its extremely simple to fathom. Imo, users normally prefer/demand configuring blogs on subdomain or at a subdirectory, which must be in the Readme of such products.

Incidentally, can anyone please help me with a .net problem

Thanks. :)
October 27, 2013 0:44
Thanks for the mention!
October 27, 2013 18:17
you should have a VPN & a Sim card of other country.then maybe problem solved.
October 27, 2013 20:24
Grrr, I was hoping to be the first to figure out how to get Ghost running on Azure Websites :)

When I tried a few weeks back I ran into two issues. I was never able to get Azure to look for index.js instead of server.js as the entry point in the package.json, but it looks like I was going about that the wrong way anyway. The other was the Azure Websites defaults to Node 0.6, again I used the package.json to switch Azure to use 0.10, but I don't see this in your config, did Azure change recently to default to 0.10?

Regardless, glad to see a good walkthrough on how to get things working, thanks.
October 28, 2013 15:01
Why Ruby is a dependency? I'm just curious.
October 28, 2013 21:31
@Pol - Ruby is a dependency because the grunt execution uses the SASS compiler gem built on Ruby
November 08, 2013 9:27
Ghost is awesome platform, especially there is no performance issues with it. We made performance test Ghost and Wordpress to compare them. Ghost handles 1000 concurrent users and Wordpress handles less than Ghost.
December 07, 2013 10:19
thanks to post this easy installment method
December 09, 2013 15:37
Stefan, I had the same issue as you and fixed it by adding readdirp to my Packages.JSON file and running npm install. I also added unidecode. I was able to get things up and running for one site, but not able to recreate a successful deployment until adding those packages.
December 17, 2013 3:04
I'm not the only one that uses 'poop' in config files. Made me giggle.
December 19, 2013 23:53
I've followed the steps a number of times, but no in my logs I'm seeing the following error:

ERROR: ENOENT, readdir '\\\volume-17-default\7841e7309a268728f78a\2c1c489d8fea4f53aec12a5e0feb5993\site\wwwroot\content\themes'
January 23, 2014 14:29
Hi Scott, have you - by any chance - tried to upgrade Ghost to the latest 0.4 release (13th of Jan 2014)? How can this be achieved on Azure Websites? Is it even possible? Thanks

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.