Installing Ghost on Azure Web Sites

If you're looking for basic, simple, shared hosting for Ghost, then Azure Web Sites might be what you're looking for. They even have a free! as in beer option... and who doesn't love free beer?

I love beer.

I digress, let's get to it.

First and foremost, big thanks to William Dibbern of the Ghost development team. He and I worked together to solve the issues we saw with Express and Express-hbs because of the Azure UNC paths. He was always quick to respond and handled the dirty work of submitting issues and pull requests. Thanks, William, we ought to have a beer together sometime.

That said, there are quite a few ways to get Ghost up and working with Azure. This post will touch on the following two:

  • GitHub
  • Git

Lastly, this post assumes you're using the SQLite option. If you'd like to use MySQL, it's certainly an option with Azure, but this post does not cover the setup for that piece of it.

You'll need an Azure account, so if you don't already have one, go ahead and create one. This, of course, will also require you to have a Microsoft account, so if you don't already have one, go ahead and create one.

If you just signed up, welcome back.

Create an Azure Web Site

First things first, let's create an Azure Web Site. Login to the Azure Portal. Click on 'Web Sites' and 'New' at the bottom of the screen and select 'Quick Create'. You'll be prompted to enter a URL or name for your web site. This must be unique to Azure and your site will be accessible using the name you provide in the format of yoururlname.azurewebsites.net. You'll still have the option, at any time, of choosing to use a custom Azure domain.

Create Azure Web Site

In under a minute, the web site should be created and you'll see the notification appear.

The web site will be defaulted into 'Free' mode. If you want a custom domain, you'll have to upgrade to 'Shared' mode. Pricing for Azure is reasonable, and as of this writing, 'Shared' mode is under $10 per month.

From here, select the web site and enter into the dashboard.

Configure Azure FTP

On the dashboard, click on 'Set up deployment credentials'.

Azure Dashboard

You'll be prompted with a dialog to enter a user name and password. You'll use these credentials when you FTP to the web site. Store these somewhere. Preferably somewhere secure. You'll need them in a bit.

If you were to create multiple Azure Web Sites, you can use this same user name and password for FTP access across all of them.

When you FTP to your Azure Web Site, your user name will be in the following format:

[AzureWebSiteUrlName]\[UserName]

Deployment from GitHub or Git

This step assumes you have your Ghost source code hosted in GitHub or a local Git repository.

Before you proceed with this step, do the following:

You'll have to bump the Express version in the package.json (found in the root of the Ghost source code) from "express": "3.3.4" to "express": "3.4.0". If you're reading this and the version is already 3.4.0 or greater, you have nothing to change. This requirement is because of an Azure bug in Express that was resolved in 3.4.0.

Ghost configuration

If you haven't already done so, copy the Ghost configuration file, config.example.js, over to a new file named config.js.

You'll want to update the production property within config.js. Swap out the value of url with the URL web site that you created. You can copy that value from 'Site URL' under the dashboard of your web site. Additionally, you'll want to set the port to process.env.PORT.

See the Ghost documenation for setting up email.

Your production property should look something like this:

production: {
    url: '[insert your Azure Web Site URL here]',
    mail: {},
    database: {
        client: 'sqlite3',
        connection: {
            filename: path.join(__dirname, '/content/data/ghost.db')
        },
        debug: false
    },
    server: {
        host: '127.0.0.1',
        port: process.env.PORT
    }
}

Initialize Ghost

Within the Ghost folder, install Grunt, npm install -g grunt-cli, bring in the Ghost dependencies via npm install, precompile the Sass and Handlebars via grunt init and finally minify the JavaScript with grunt prod -- all in that order.

Create a new file in the Ghost root called server.js and add this line of code to it: var GhostServer = require('./index'); This file is required for Azure Web Sites to detect that it is a Node.js application.

Commit all changes to your Git repository. Note that the .gitignore will exclude core\built\, core\client\assets\ and config.js files. These files are required, so I'll leave it up to you if you wish to bypass and commit them or FTP them to your site after deployment.

Deployment

Next up, select 'Set up deployment from source control' on the Azure Web Site dashboard.

Here you can select 'GitHub' or 'Local Git repository'. If you choose 'GitHub', keep reading otherwise use this Azure Node.js Git tutorial and return to the Azure Configuration section below after the publish.

Azure GitHub Deployment

When you proceed, you'll be prompted to log into GitHub and accept the permissions that Azure is asking for. Afterwards, you'll be prompted to select the repository containing your Ghost source code and the branch that you'd like to target.

Azure Deploy Repository

Assuming success (GitHub or Git), you'll see something like this:

Azure GitHub Deploy

Note that Azure automatically processes the package.json and pulls down the submodules. Lastly, as you make changes to your GitHub or Git repository, you can seamlessly sync those changes without having to run thru this blog post again. Yay!

Azure Configuration

Azure Web Sites have iisnode baked into the IIS pipeline. It allows you to run Node.js applications within IIS. If you love Node, you should say hello to iisnode - it has some benefits. I should probably blog about it - it's impressive in its own right. Anyways, this works to our advantage because we don't have to install and/or setup Node.js on the Azure Web Site, it's just there waiting for us, in stealth, if need be.

Node.js environment variables

When Ghost runs, it reads from config.js to determine, for example, where to read/write data from. Assuming a production environment, you'll want set the Node.js environment variable, NODE_ENV.

Click on the 'Configure' tab on your web site and scroll down till you see 'app settings'. Add in NODE_ENV with a value of production. Do not forget to click 'Save' at the bottom of the screen!

Azure App Settings

Azure web.config and iisnode.yml

Alright, it's time to FTP. Grab the FTP credentials that you stored in a secure location earlier and open up your favorite FTP client. Copy the FTP URL from your web site dashboard under 'FTP host name'.

Open up your FTP client, enter your credentials and connect to your Azure Web Site.

If you didn't already commit core\built\, core\client\assets\ and config.js files to Git, now would be the time to FTP them.

This is completely optional, but you might be interested in logging your output and error streams. You can do so by adding the following within the /site/wwwroot/iisnode.yml file:

loggingEnabled: true
devErrorsEnabled: true
logDirectory: iisnode

In this context, logs will be found under /site/wwwroot/iisnode/ - you can change this to whatever makes you happy.

We're almost done!

Hack for Azure UNC Support

Update: If you're using a version of Ghost that uses express-hbs 0.5.0 this step is no longer needed. Hooray!!

This step is only required if, within the package.json of your Ghost source code, you are running "express-hbs": "0.2.2". The first release of Ghost, 0.3.0, uses this version.

An upcoming express-hbs fix should soon negate this step. However, I have not tested it yet. I'll be sure to do so when it's released. Stay tuned to this blog for further information about this issue.

If you're new to Node, I don't condone what I'm about to show you. If you're not new to Node, ummm, close your eyes, skip over the next section and spare me in the comments.

That said, there are other ways to do this, but a hack is a hack is a hack... and we all do it from time to time.

Update: The pull request above was not entirely complete. If you are using Ghost containing express-hbs 0.3.0, replace /site/wwwroot/nodemodules/express-hbs/lib/hbs.js with this hbs.js from a pull request that will, hopefully, soon negate this hack all together. If you are using a version of Ghost containing express-hbs 0.2.2, continue with the steps below.

Edit /site/wwwroot/node_modules/express-hbs/lib/hbs.js.

Swap out the cachePartials() function with the following.

function cachePartials() {
    var files = fs.readdirSync(partialsDir);
    files.forEach(function (file) {
        var stats = fs.statSync(partialsDir + '/' + file);
        if (!stats.isFile()) return;
        var source = fs.readFileSync(partialsDir + '/' + file, 'utf8');
        var name = path.basename(partialsDir + '/' + file, path.extname(file));
        exports.handlebars.registerPartial(name, source);
    });
}

That's it.

That wasn't so bad, was it? That said, please don't ever associate my name with the aforementioned. Too late, eh?

In all seriousness, if you don't know any better, usually you want to avoid hacking your node_modules. These are your project dependencies which are not typically stored with your application source code. They are automatically pulled in based on your package.json via an npm install.

Instead, if possible, find a work-around in your application or submit a pull request to said module and begin using that version in your package.json. (Says the guy who did neither... my options are limited and, while I'd love to help out, I'm not on the Ghost development team.)

Conclusion

If you successfully followed my instructions, you can navigate to the URL of the web site you just created.

Ghost, simple, beautiful, blogging, is ready for ya!

Boom goes the dynamite!

(If you receive an error when navigating to the URL, try restarting the web site within the Azure Web Site Portal.)

Any questions, comments, concerns or free beer, please reach out to me via Twitter and/or the comments below.

Cheers!


comments powered by Disqus