Hosting Ghost on Azure — The Definitive Guide

Andrew Zey
Dec 10, 2014 · 9 min read

There are numerous ways Ghost can be hosted on Microsoft Azure, each with their own set of pros and cons. In this guide, I’ll describe the shortcomings of these methods, and propose a new solution that avoids their pitfalls.

Existing Methods:

Azure’s Gallery Template



  • cannot use Git version control because of these problems:
  • No .gitignore file. This means that all npm dependencies are version controlled. Updating packages on one platform (x64) may cause problems on Azure, which is x86.
  • The ghost.db file is version controlled, but Azure doesn’t commit changes. Every time you push from local to Azure, you will overwrite your database.

Deploy from Third-Party Azure-Friendly Ghost Repos

This method has you fork a third-party repo (not maintained by Ghost team) to deploy your blog. Here are 2 of such guides:

Kevin Meurer’s Ghost on Azure Tutorial

Cho S. Kim’s Ghost on Azure Tutorial


  • more control than Azure gallery template
  • can edit themes locally


  • dependent on third-party to update fork to get Ghost platform updates
  • having blog source on GitHub means you have to setup separate environment variables for any third-party credentials and API keys (like Mailgun, Disqus, etc.), since you don’t want those publicly visible

Other Git-based Guides with Local Repo and Azure

Cons — don’t have all blog content in source control — hard to upgrade, depending on which guide you follow

The Better Way (in 7 steps)

I will show you a setup that enables 3 convenient workflows:

  1. Easy to upgrade directly from official Ghost github repo
  2. Can create and edit posts on the deployed blog
  3. Blog runs locally for easy theme / settings development

Step 0 — Prerequisites

You should have the following things:

  • Node.js installed locally
  • Git installed locally
  • Microsoft Azure Account
  • Text Editor

Step 1 of 7: Clone Ghost Blog Locally

The first thing we will do is clone the official Ghost blog repo locally.

Open a terminal, and type the following commands:

$ git clone # rename the cloned folder to your own site name 
$ mv Ghost yourSiteName
$ cd yourSiteName # rename the remote repository from 'origin' to 'upstream'
$ git remote rename origin upstream

Step 2 of 7: Configure Ghost Blog to Run Locally

The next step is to build the files necessary for Ghost to run locally.

# checkout the stable release branch. Don't use 'master', as it contains unreleased code! 
$ git checkout stable
# install Grunt command line interface
$ sudo npm install -g grunt-cli
# install all node dependencies
$ sudo npm install
# run grunt task that builds all files needed for development #if you plan on editing Ghost core files, run:
$ grunt init
# OR #if you only plan on editing Ghost theme files, run:
$ grunt prod # start your ghost blog $ sudo npm start

Now open http://localhost:2368 in your browser, and you should see

Image for post
Image for post

Step 3 of 7: Create Azure Site (that will host your Ghost blog)

If you haven’t already, sign up for an account on Microsoft Azure. That can be done here:

Once you’ve done that, navigate to your Azure Portal (, and you should see a page that looks like this:

Click on “NEW” in the bottom left to create a new website. Then select “WEBSITE” and “CUSTOM CREATE”.

Image for post
Image for post

On the next screen, type in the name of your website, specify your region (make sure it’s the closest one to your location!), no database, and select “Publish from source control.”

Image for post
Image for post

On the next screen, select “Local Git repository”, click next, and confirm your selection.

Image for post
Image for post

You will now be returned to your portal view, where you should select your website, which will take you to the website overview. Select “CONFIGURE” from the top menu bar.

Image for post
Image for post

Scroll down until you get to a section titled “app settings”. This contains your website’s environment variables (accessible in your Node.js code via process[‘nameOfVariable’]). We are going to add a NODE_EVN variable with the value “production”.

Image for post
Image for post

Now scroll back up to the top of the page and select “Dashboard”.

Image for post
Image for post

You should arrive at a page that looks like this:

Image for post
Image for post

Look for a button called “Reset your deployment credentials”, which will take you to a screen where you can choose a username and password. These are the credentials you will use to authenticate via git.

Image for post
Image for post

That’s it, your Azure site is now ready!

Step 4 of 7: Configure Your Local Repo for Pushing to Azure

The next thing you’ll have to do is add your azure repo as a remote repository. To do this, navigate to your website in your Azure portal, and select the “Deployments” button from the top navigation. That will take you to a page that looks like this:

Image for post
Image for post

Click the button to the right of your git url to copy the git url to your clipboard.

Now, back at your terminal, inside your ghost repo, type the following command:

$ git remote add azure <paste your git url here>

Step 5 of 7: Configure Ghost Repo to Work on Azure

The first thing we’ll do to configure our Ghost repo, is to add our new Azure website url to our config.js file, which is in the project root. For some reason, this file is ignored by git by default. We’re going to have the change that.

Open the .gitignore file in your text editor, and comment out the following lines (by prepending a ‘#’):

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

Now open the config.js with your text editor, and you will see a file that looks like this:

Image for post
Image for post

In the “production” section, replace the url with the url of your Azure website. Also replace the default port number with “process.env.PORT”.

Now we’ll need to add 2 files (web.config and iisnode.yml) to tell Azure how to deploy Ghost. As Chris Fraser explains:

Azure is using iisnode behind the scenes to do a whole lot of magic, but here is a basic rundown of what is going on:

1. You are pushing to your Git repo to Azure, which sits in a folder called repository, next to your wwwroot folder

2. A deployment is triggered and a deployment script is generated

3. KuduSync.NET deals with copying your files to wwwroot

4. iisnode restores all your required packages and starts Ghost

The first file we will create (in our project root) is “web.config”. Paste the following contents into this file, and save it:

<?xml version="1.0" encoding="utf-8"?>  
This configuration file is required if iisnode is used to run node processes behind
IIS or IIS Express. For more information, visit:

<!-- Visit for more information on WebSocket support -->
<webSocket enabled="false" />
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="index.js" verb="*" modules="iisnode"/>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^index.js\/debug[\/]?" />

<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>

<!-- All other URLs are mapped to the node.js site entry point -->
<rule name="DynamicContent">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
<action type="Rewrite" url="index.js"/>

<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<remove segment="bin"/>

<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />

You can control how Node is hosted within IIS using the following options:
* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
* node_env: will be propagated to node as NODE_ENV environment variable
* debuggingEnabled - controls whether the built-in debugger is enabled

See for a full list of options
<!--<iisnode watchedFiles="web.config;*.js"/>-->

The second file we will create in the project root is: “iisnode.yml”. Paste the following code into the file, and save it:

node_env: production  
loggingEnabled: true
logDirectory: iisnode

Step 6 of 7: Build our Ghost Files for Deployment

Now all we have left, before actually pushing our blog up to Azure, is to build the production files that will actually be hosted. To do this, run:

$ grunt prod

Step 7 of 7: Commit and Deploy via Git!

Okay, we’re almost there! Now you just need to commit all your changes, and push up to Azure! From your console (again, make sure you are on the “stable” branch), enter the following commands. Thanks to Kevin Meurer for instructions on solving the line ending error some people experience):

# add changes to staging area
$ git add --all

# If you receive an error related to the
# core/built files, run the following
# commands. Otherwise, skip to the commit.

# We need to convert our /core/built/scripts files from dos2unix.
# To do so, we will use the dos2unix command.
# First, make sure you have the command installed.
# You can install it with homebrew as shown below.
# If you don't have homebrew installed, do the quick install
# from their website.

$ brew install dos2unix

# Now, lets move into our core/built/scripts library

$ cd core/built/scripts/

# Finally, we convert all script files in
# the library to unix-compatible
# adaptations. Make sure you're in the
# right directory for this command
# (which you should be if you ran the last command).

$ find . -type f -exec dos2unix {} \;

# Now, make sure you're in the root of your ghost azure directory
$ cd ../../..

# As before, we add all files
$ git add --all

# End of error fix.

$ git commit -m "Initial commit"
$ git push azure stable:master #this pushes your local stable branch to azure's 'master' branch

Woohoo! We’re finally there. The terminal will do a whole bunch of things, and once it’s done, you should be able to visit your deployed blog at your azure websites url!

Image for post
Image for post

Upgrading Your Blog to the Latest Version

As of the time of this writing, I haven’t been able to test my workflow for upgrading my blog, but the idea behind keeping the official repo as an upstream is to make it as simple as:

  1. pull from the upstream and resolve merge conflicts (if any)
  2. run npm install
  3. run grunt prod build task
  4. commit and push to azure

Once I’ve had a chance to test out the workflow, I’ll update the instructions here!

Next Steps

Closing Notes

At last, we have a sensible workflow for hosting a Ghost blog on Microsoft Azure. While this method solves most of the problems associated with previously defined methods, there’s still one crucial aspect missing: backups. None of the methods described will ever result in a git commit being a full backup of your blog.

For now, I’ll be manually FTPing in periodically and backing up my wwwroot folder. At some point, I plan on writing an automated backup script for Ghost on Azure. I will update this post with a link once I do.

I hope you found this guide useful. I’d love to hear any comments or questions you may have!

Originally published at on December 10, 2014.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store