Menu

The Ghost in the Machine (part 2)

The new, improved "How to" for setting up a Ghost blog on Heroku

When I first set up this blog, I wrote this post as a step by step instructional for hosting a Ghost blog on Heroku. I'm writing this now to say, "Wait! There is an easier way!" I came across this Easier WayTM when trying to update the version of Ghost I was using. I knew Ghost had added a bunch of new features, including Preview mode (which I was keen to incorporate), so I wanted to pull in the latest version. If you haven't read that previous post, the tl;dr is "Wow, there are a lot of things that can go wrong. And they will. Repeatedly." As I started trying to update Ghost in my fork of the TryGhost repo, I remembered just how painful it was to do this the first time. Particularly submodules. Anyway, suffice it to say, I was attempting to follow more or less the same set of steps as before and was encountering the same resistance. So I decided to work smarter instead.

New Steps for setting up Ghost on Heroku

Configure Ghost

The steps for setting up your Ghost configuration (e.g. adding a mail server and a database connection, etc.) are still the same. I basically had two commits in my new heroku branch (I blew away my old stable-local branch, which, due to repeated attempts to deal with git submodules, was beyond salvage): One to add config.js, which was just a copy of config.example.js but with additional configuration (as specified in my previous post); and one to set the install script in my package.json. Originally, I had the install script set like this:

scripts: {
install: "grunt shell:bower default prod"
}

This was the equivalent of grunt init prod, but without the update_submodules task, which fails on heroku because it's not a git repository. grunt init use to simply run shell:bower update_submodules default, but it was updated at some point in Ghost's history to be shell:ember:init shell:bower update_submodules assets default, so I made my script mirror that, but without update_submodules and with prod on the end.

I also did add one additional commit to copy my forked and modified version of the Casper theme (newly-dubbed Dullahan) into content/themes, as well as a pure, unmodified fork of Casper (since the submodule would not exist on heroku). Note that Ghost's gitignore includes an entry for content/themes/** so you will have to git add content/themes/yourTheme -f to stage it in git.

Add github-based deployment to your Heroku app.

Go to your Heroku dashboard, click on your app, and then click on the Deploy tab.

I was originally using the "Heroku Git" deployment model, also known as the Way of Pain. Instead choose "Github" and feel better about life.

Of course, yours will not say "Connected" yet. You should have a button to do this though. Go ahead and do it. The authorization screen is a bit scary (it wants access to everything: public and private repos, including any repos of organizations you're a part of; the social security numbers of all your children . . .), but it turns out that it only uses this phenomenal cosmic power to build a list of repos to which you can connect your app. Back in Heroku-land, choose your github username from the dropdown (if you're a part of any organizations, they show up here as well), type in the repo name, and click Connect.

Deploy. Yes, really.

You can enable automatic deployments for a branch so that, whenever code is pushed to that branch, it's deployed to your Heroku app. I opted not to do this, as it felt a little risky. I routinely commit and push code to branches in unfinished states, so I chose to use Manual deployment instead. Scroll down to the last section on the page, choose the branch you want to deploy from, and click Deploy Branch.

If everything is configured correctly, the deployment should succeed and you should have a working Ghost blog on Heroku. It really was that easy for me, and the best part is that the next time I need to update Ghost, it should be even easier. My new heroku branch has almost no changes that could cause conflicts. All the changes are file additions except the install script in package.json, and that part is easy enough to resolve in a conflict.