From WordPress.com to Ghost on DigitalOcean

After ~3 years of managed hosting at WordPress.com I decided to self host my blog.

In this blog post I’ll outline all the steps I took to migrate.

Why WordPress.com?

Three years ago I decided I want to create a blog, but didn’t have any specific platform in mind. WordPress was easy to setup, so I just rolled with it.

Why Move?

Managed WordPress is problematic for a programmer.
Many times I find myself wanting to install a specific plugin or theme, but can’t.
To gain all the features WordPress can offer, I need to pay. a lot.

I don’t mind setting up everything. like I said before, I believe that having strong sysadmin skills (and being able to administer your servers) is a must have.

Where should I move?

This question has two parts:

  • Should I stay with WordPress or move to a different platform?
  • Where should I host my blog?

Server Hosting

I started looking into my options. and they were so expensive.
At WordPress I paid ~2$ a month for a managed blog, while most other providers I considered wanted at least 15$.

Then I found out that DigitalOcean has a WordPress Droplet but also a Ghost Droplet. Both cost 10$ a month.

Platform

WordPress is much more than a blogging platform, while Ghost is just that.

Ghost is a fully open source, hackable platform for building and running a modern online publication. I read a lot about Ghost during its Kickstarter campaign, and decided to give it a go.

I fired up a ghost docker container and started playing with it. It was awesome!

Migration

Step 1 - Move all the data

Sounds like an easy task, right? Theoretically, Ghost has you covered.

All you need is to install Ghost plugin for WordPress and in a few minutes you’ll have a dump of your entire blog in Ghost format.

Well, I couldn’t install this plugin because I’m on a managed WordPress account with limited plugin availability. Did I Give up? HELL NO!

First, I fired up a local copy of wordpress, using docker-compose:

version: '2'

services:

wordpress:
image: wordpress
networks:
- wordpress
ports:
- 8080:80
volumes:
- /path/to/volume:/vol
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: dev
WORDPRESS_DB_PASSWORD: dev
depends_on:
- mysql

mysql:
image: mariadb:10.0
container_name: db
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_USER: root
MYSQL_USER: dev
MYSQL_PASSWORD: dev
MYSQL_DATABASE: wordpress
networks:
- wordpress

networks:
wordpress:

Actually, I had major issues with the database, until I figured out that the latest mariadb image is broken.

Once I had WordPress up and running, I migrated my blog. thankfully, WordPress also migrates all the photos for you which makes things really easy!

Then, I installed the Ghost plugin and exported all the data to my local ghost instance.

I also needed to manually move all the media, because ghost doesn’t support that at the moment. You can read more WordPress -> Ghost migration here.

Step 2 - Rewrite all the posts

Ghost uses Markdown, while WordPress uses a proprietary, non-conformant syntax language.

The Ghost plugin did an OK job at migrating my posts, but I had to manually correct them. This took a while, but I also fixed many old posts and added proper tags to all of them.

I also had to add support for <!-- more --> tag in Ghost, because they wouldn’t do that now. They’re looking into finding a better solution.

Hacking into Ghost to add support for <!-- more --> was really easy!

Step 3 - Setup Ghost on DigitalOcean

DigitalOcean are really awesome. They’ve got tutorials for everything.

  1. It took me five minutes to setup my vanilla ghost blog, using this tutorial.
  2. Then another five minutes to setup SSL certificates with Let’s Encrypt, using this tutorial. I also added Cloudflare.
  3. Another minute or two to update my DNS records on namecheap
  4. Transferring my domain name was easy too. I followed this tutorial.
  5. Setting up MailGun, was again, a trivial task.
  6. Finally, I uploaded the blog to my droplet (how? there’s a tutorial for that)

And that’s it! I’m writing this blog post on my new, shiny, Ghost-powered blog, served through Cloudflare to my tiny DigitalOcean droplet :)

Aftermath

Ghost doesn’t have a plugin infrastructure to trigger updates to social profiles on new posts.

There are many solutions online, but most of them required hard-coding support to ghost core or my theme.

I went on a different approach: leverage IFTTT. It’s extremely easy to write a new applet, and there are hundreds (if not thousands) of pre-baked recipes you can use.

I ended up writing my own applet: If new feed item from my blog, Then post a tweet on my behalf.