In an effort to make my blog suck less, I figured it was time to get onto a modern blogging platform. I've been blogging for over 10 years and was getting frustrated at the lack of control I had over my current blog engine. Add the fact that Google is now giving priority to mobile-friendly sites, and it was clear it was time for a change - since I wasn't able to control the lack of mobile support in my blog, among other things.
Which Blog Engine?
There are so many good blogging engines out there now - which one to choose? I really love Markdown so I started looking at blogs that supported Markdown. The two blog engines that kept popping up with markdown support were Jekyll and Ghost. Jekyll can integrate with Github and Github markdown which is interesting. However, I really love Node and Ghost is written in Node which put that to the top of my list - if I wanted to make customization, it would be easiest for me with a Node code base. It's actively being developed with a great developer community which sparked my interest.
There are several good hosting options for Ghost blogs but I really wanted to control everything myself with Azure. Azure is an awesome option for hosting Node applications. A couple months ago, Scott Hanselman blogged about installing Ghost to Azure with the "Deploy to Azure" button. This option specifically uses the Ghost-Azure Github repository.
There are two main benefits to the Ghost-Azure repository. First, it sets up everything for Azure deployment through the azuredeploy.json file and the "Deploy to Azure" button. Second, it sets up Ghost for an IIS deployment in general - these are things like the web.config file and iisnode.yml. Using this directly is a great option if you just want to get Ghost up and running on Azure quickly for test purposes. But this doesn't quite fit my needs because I wanted to be able to make any customization to the code should the need arise. So my first step was to fork this repository into my own Ghost-Azure Github repository.
Once this was complete, I just did a normal setup for an Azure Web App with Continuous Deployment from a Github repository. This automatically deploys the latest code anytime I check-in to my Github repository. I never actually had to use the "Deploy to Azure" button.
With Ghost in place, the next item on my list was to get a good theme. There are a few great sites for getting Ghost themes including the Ghost Marketplace and All Ghost Themes. I got a paid theme from All Ghost Themes.
Moving my static files to the Azure CDN was one of the most important aspects of moving my blog. The Azure CDN allows me to have better performance by serving content to consumers from geographic locations closest to them.
First I mapped my CDN to a custom domain - cdn.stevemichelotti.com.
Then I created the following containers in my blob/CDN storage:
Using the CDN I'll also be able to optimize expires headers of static content. Additionally, if I ever move my blog again, I won't have to transfer images since they're now all on my own custom domain for my CDN.
Moving content was more of a pain than I anticipated. There are several tools to migrate to Ghost from other platforms (e.g., Blogger, WordPress, and Tumblr). However, none of them worked with my existing platform. So it was a somewhat manual process for me that consisted of:
- Writing a local Node script that iterated through all my old posts and converted them to Markdown using Heck Yes Markdown.
- Save each Markdown file locally, including the title and publish date in each file name.
- Import all Markdown files into Ghost using the Admin tool.
For the most part, this worked fine. But I did have to go through many old posts to ensure the formatting was satisfactory.
Disqus for Comments
Disqus is a strong standard for a comment engine for blogs these days. The setup process was quick and integrating this into my blog was very straight forward.
App Insights and Google Analytics
I've currently got both Application Insights (from Microsoft) and Google Analytics integrated. Perhaps I'll consolidate to just one of them in the future - but for now I'm going to use both.
Code Syntax Highlighting
There are a number of good choices for code syntax highlighting. In the past, my choices were limited because of some lack of control I had on my previous platform. Since I now have full control, I decided to use SyntaxHighlighter. It is extremely full-featured including line numbers and line highlighting support. It also supports brushes for a number of different languages.
Originally, I thought Feedburner would be a trivial step - after all, all I needed to do was to go into my Feedburner account and burn the feed for my new blog location. However, I did run into one complication. I wanted to make sure I supported feed auto-discovery but including:
<link rel="alternate" type="application/rss+xml" title="Steve Michelotti" href="http://feeds.feedburner.com/SteveMichelotti" />
However, Ghost was hard-coded to include:
<link rel="alternate" type="application/rss+xml" title="Steve Michelotti" href="http://stevemichelotti.com/rss/" />
The good news was that, since I have now have full control over the source code for my blog, I could fix this myself. I simply went into ghost_head.js in the core code and removed this line of code:
head.push('<link rel="alternate" type="application/rss+xml" title="' + title + '" href="' + config.urlFor('rss', null, true) + '" />');
Then I manually added the correct
<link> element to the
<head> section of my site.
What about Live Writer?
It seems everyone on the Windows platform loves using Windows Live Writer to author blog posts (including me). But Live Writer doesn't support Markdown. Frankly, this is just fine with me. I think people in general that love Markdown like writing directly in Markdown. Also, in place of Live Writer, I now use MarkdownPad to author posts offline. MarkdownPad is the best Markdown editor I've seen and I love using it.
So far I'm very happy with the move. Time will tell - but it's great to finally be on a modern blogging platform with complete control over the code and environment.