Migrating DailyJS to Ghost

2015-04-23 18:35:33 +0100 by Alex R. Young

I've been running DailyJS since 2009 using Jekyll, but for the last few years it's made writing more difficult in many ways. The main issue I had was the time it takes to generate the site--it was at about 15 minutes on my VPS, which meant any correction took an inordinate amount of effort.

Running a static site was great for server efficiency, but not friendly for writing. Using a CMS-style blog engine definitely makes writing and supporting contributors easier, so I decided to switch.

The blog engine I've ended up using is Ghost, which is written with Node. I've hosted it within an Express app so I can set up redirections for old URLs--Ghost doesn't have an in-built redirection system. Because you can run Ghost as a standard npm module, I came up with this short program:

var ghost = require('ghost');
var express = require('express');
var app = express();
var redirects = require('./redirects.json');

redirects.forEach(function(redirect) {
  var newUrl = redirect[1];
  if (!newUrl.match(/^http/)) {
    newUrl = 'http://dailyjs.com' + newUrl;
  console.log('Redirecting %s to %s', redirect[0], newUrl);
  app.get(redirect[0], function(req, res) {

ghost({ config: __dirname + '/ghost-config.js' }).then(function(ghostServer) {

I have JSON file that's just an array of URLs (redirects.json), and it uses this file to set up an Express route handler for each redirection.

The theme I've used is the Astro Responsive Theme which is commercial--I paid $19 which included a $2 charge for using PayPal. I customised the theme so it includes the advertising I use to fund DailyJS. The sidebar where I want to show the ads is hidden on smaller screens, which would probably cut a huge amount of revenue, so I had to write some client-side JavaScript to detect this and move the ads. This may need to be improved upon later!

Other things that still need fixing includes links to tags: Ghost has proper support for tags, so we no longer need my hacked Jekyll tags.html#tagname file. I need to somehow remap all of these links to /tag/tagname.

The thing that everyone likes about Ghost is the admin area--the editor is very friendly, and it supports pages, top-level navigation, and data import/export.

I actually used the data import feature to import my old posts. I wrote a Node program that pulls in Jekyll posts, strips the YAML frontmatter, converts it to Ghost JSON, and then maps all the relationships for tags and authors. This took quite a long time because I had to also convert older Textile posts to Markdown. I used Pandoc for that, which is awesome, except it forces Textile to use smartquotes, which means all of my code examples got messed up. I had to build a version of Pandoc that turned smartquotes off just for this purpose.

The JSON for the old site ended up being 5 MB, which I wasn't able to import into Ghost when it ran on Heroku. To get around this, I tried connecting Ghost running locally to Heroku's database, but it wouldn't work that way either. It kept timing out without a clear error. As a last resort, I ran Ghost connected to a local PostgreSQL database, then restored Heroku's database from an export of the local database.

In the end migrating DailyJS to Ghost was lots of work, and there are still more issues to iron out. I really hope readers prefer the new site, I think the design is cleaner and the tags are definitely better. If you see anything you think is wrong, you can try contacting us through contact.dailyjs.com or just hassle me with my Twitter account (@alex_young).