Migrating BlogEngine.NET to Ghost

As mentioned previously, I recently migrated my blog to Ghost from BlogEngine.NET. I'll be writing a few posts discussing what I went through in the migration process.

Installing Ghost on Azure

I run my life on Azure. Previously, my BlogEngine instance was running as an App Service, so getting Ghost running on Azure was important to me. Luckily, Felix Rieseberg has already done all of the hard work for this step.

Head over to Felix's Ghost-Azure repo on GitHub and click the big blue Deploy to Azure button. The deployment scrip will run, and you'll be asked to log into your Azure account and fill out a form with your new site's information (subscription, resource group, site name, etc.). When you complete this step, a new Ghost instance will be deployed to the location you requested. Simple!

Be sure to note the instructions on setting this up on a Basic (or higher) App Service Plan, then backing down to a Free/Shared plan if desired. This gets past a limitation where NPM requires more resources than a Free/Shared plan will give.

Once setup, you can hit your Ghost instance by browsing to [site name].azurewebsites.net . It may take a while to spin up the first time as Ghost is building its initial database, but give it some time. If you get a blank page, just refresh once or twice. You can also get to the Ghost admin page by browsing to [site name].azurewebsites.net/ghost.

In addition, deploying with this method will also setup Felix's repo as the deployment source, so when Ghost is updated and Felix integrates those changes into his repo, you can upgrade your instance simply by sync'ing with the repo. I've already done this once (v0.11.7 to v0.11.8) and it worked exactly as expected. You'll find the Sync button in the Azure portal under your App Service's Deployment Options section.

Sync button

Getting Your Old Data

Next up, you'll need to migrate your posts and files over to Ghost. Luckily this is pretty simple, also.

BlogEngine Data

First, on your BlogEngine site, navigate to /admin/#/settings/tools/export or /admin/#/settings/advanced depending on your version of BlogEngine. On this page, you'll find a button to export your data to the BlogML format. Click the Export button to download the file, and save it somewhere locally.

BlogEngine Files

This step really depends on your current hosting provider, but I'll speak to Azure. Since I was hosting BlogEngine via an App Service, I was able to use Kudu to download a zip file of my entire site. To do this, browse to [old site name].scm.azurewebsites.net, which will load the Kudu interface. From here, click on the Debug console > CMD menu at the top of the screen. This will load a page where you can navigate the file system of your site as well as execute commands. From here, click on the site folder and then click the download icon next to the wwwroot directory.


This will zip up the entire wwwroot directory and download it, which is a nice backup. But, we're only interested in one directory for now. So, open/extract the zip file and navigate to the App_Data/files directory. This will contain all of the images and used in your posts.

Now, switch over to your new Ghost blog's Kudu site ([site name].scm.azurewebsites.net) and navigate to /site/wwwroot/content/images. Drag and drop the contents of your extracted App_Data/files directory to the browser window, which will upload all of the files.

If you have a LOT of files, you may need to do this in chunks. Alternatively, you could upload a zip file of your images to the site and then use 7zip, which is automatically installed on your site D:\7zip\7za.exe, to extract them to the proper location.

If you were using a directory to store any static files that aren't images, create a new directory inside /site/wwwroot named public and copy those files here. These files will be available externally at the root of your site. For example, if you have a file named MyFile.txt located in /site/wwwroot/public, it would be available at http://yoursite.com/MyFile.txt.

Updating Your Old Data

BlogEngine uses HTTP handlers (.axd files) to load images and files. These links need to be updated to be Ghost-friendly. The easiest way to accomplish this is to load the exported BlogML.xml file into your favorite text editor, and do a find/replace:

  • Find /file.axd?file= and replace with /,
  • Find /image.axd?picture= and replace with /content/images/.

The former will re-point your static files to the public directory we created above, and the latter will re-point your images to the content directory for Ghost, where you copied our files previously.

Uploading Your Old Posts to Ghost

I tried a few tools to migrate BlogML to Ghost's JSON format and found BlogEngine2GhostConverter on GitHub to work well. Once you've built the app (or download my binary here), simply run the executable like this, passing in your BlogML file and where you want the output to go:

BlogEngine2GhostConverter YourBlogML.xml GhostImport.json  

Now, go to your Ghost admin page ([site name].azurewebsites.net/ghost) and select the Labs tab. Here, click the Browse... button under the Import section, locate the JSON file created above, and then click the Import button. If all goes to plan, your entire dataset will be imported into Ghost and will be immediately viewable. If you uploaded your images and made the changes above, all those should also be working. Neat!


The default Ghost configuration file, config.js, looks for a few environment variables for the site's URL, email, etc. The easiest way to set these up is with the Application settings section of your App Service in the Azure portal. If you are using a custom domain for your site, you'll just need to setup the websiteUrl item. In the App settings section, add a new variable named websiteUrl and set it to your custom domain.


If you have an SSL certificate setup already, you can also add an entry for websiteUrlSSL and point it to the https URL for your site.

If you are not using a custom domain at all, you can skip this step.

URL Rewrites

Now that your blog is migrated, you will want to create a few URL rewrites to redirect users (and more importantly, search engines) to the new locations for your posts and pages. By default, BlogEngine places all blog posts under the /post URL and all static pages under the /page URL. Ghost puts everything at the root. So, let's create two rewrites to handle these.

Back in Kudu for your new Ghost site, navigate to /site/wwwroot and click the Edit icon next (the pencil) to the web.config file. Inside the configuration/system.webServer/rewrite/rules section, add the following:

<rule name="post" stopProcessing="true">  
    <match url="^post/(.*)" ignoreCase="true" />
    <action type="Redirect" url="{R:1}" redirectType="Permanent" />

<rule name="page" stopProcessing="true">  
    <match url="^page/([^0-9].*)" ignoreCase="true" />
    <action type="Redirect" url="{R:1}" redirectType="Permanent" />

These rules instruct the server to redirect from /post and /page to the root directory, sending a 301 code which signals that it's a permanent move.

Note that the second rule above has some additional intelligence. Ghost's pagination system uses the word page in the URL, followed by a number. This rule only redirects requests that start with page but don't have a number after the slash, which should redirect only real static pages, so long as the slug/URL doesn't start with a number...

And with that, you should have a pretty functional Ghost blog setup and working. You can point your custom domain to this new site, and get rid of your old site if you wish. Be sure to save that wwwroot.zip file and the BlogML export we created earlier in case you need them. In my next post on this topic, I'll describe how to setup Disqus for comments, and you'll need that BlogML file to import your old comments.