Moving to WordPress from Jekyll

I recently told some friends that I was moving to WordPress from Jekyll. Not a surprise, the overwhelming sentiment was that hell had in fact frozen over. If you’ve followed my blog, you may remember that I’ve bounced around between WordPress and Jekyll a few times. I’ve also jumped around between GitHub, GitLab, and Cloudflare’s static site hosting solutions along the way.

Why move from Jekyll?

Let’s start by talking about why I started using and have stuck with Jekyll for the last decade.

Jekyll was always appealing to me because it always felt very developer-centric. I live on the command-line. Maintaining a Jekyll allowed me to utilize the same stack as I would if I were coding. I could write my posts in Vim. I could sling some Ruby to create more complex pages. Everything was containerized with Docker.

Going from my local development environment to production included a CI/CD pipeline to build the static files and deploy them. It all felt very familiar. Familiarity means there was a level of comfort. Being comfortable made it easy to focus on generating content, which I’ve been doing weekly for a decade now.

Being comfortable also leads to complacency, which is honestly how I’ve been feeling about my blog lately. My posts have been getting thinner, word counts plummeting, in an attempt to maintain a vanity metric of posting weekly.

Why move to WordPress?

So what was the appeal that caused me to move to WordPress from Jekyll? Jekyll definitely made it easy to be comfortable and focus on my writing. It also made some things extremely difficult. Search was one of the things that was a constant thorn in my side.

With nearly 900 posts on my blog, search was important, and with Jekyll, implementing it or leveraging site: and linking to a search engine felt half-assed. The best implementation of search on a static site that I found was Algolia. Unfortunately, it gets pricey quick as your data set increases in size.

Keep in mind, I’m no stranger to WordPress. We use it for my wife’s blog, and I’ve been dabbling with a couple of new sites recently. The big appeal has been not dealing with having to sling code to do simple things. WordPress is a blogging platform, so all the blogging bits come baked in. No more worrying about writing code to generate RSS feeds or fighting with category and tag lists.

As you run a project, your priorities change. I’ve grown tired of the vanity metric of doing weekly posts. I’ve had an itch to take my blog more seriously. That’s not to say you can’t run a serious site on Jekyll, but things do get a lot more complicated as the size of a site grows.

Jekyll’s plugin ecosystem is built around adding functionality to your site, and not necessarily around helping you to improve your site. WordPress plugins with Yoast SEO and Google Site Kit help you with your content and give you better insights. This was a huge draw for me.

Picking a WordPress host to move to

With the decision made to move my blog from WordPress to Jekyll, I needed to figure out how I would be hosting it. I absolutely hate hosting WordPress sites myself. It’s not that it’s outside of my skill set, there’s just a lot to the security side of things that I’d prefer to let smarter kids than me take care of it.

With self-hosting off the table, I needed to pick a WordPress host. I’ve used a small handful of hosts in the past, and sampled a few others while I was doing some WordPress hosting comparison posts. For all the other WordPress sites I’m attached to, we’ve been using Flywheel. Their pricing is great for smaller sites and I’ve found them to be very reliable. They are also owned by WP Engine, which I’ve used for this site in the past.

My site does a decent amount of traffic, definitely outside of what Flywheel’s lowest tier would support. Not a big deal, I could opt for the next tier up and pay a little more.

This is where Flywheel fell short for me. I currently have two sites on their lowest pricing tier. When attempting to add a third site, on the next pricing tier, I ended up stuck in the mud. Seems like they attach your plan and subscription to your account or organization, and then all sites need to use that particular pricing plan. Want to mix and match plans? You’ll need to move all of your sites up to the next tier.

I probably could have reached out to the Flywheel support team, but in doing a bit more research on the price, WP Engine was actually going to be more economical based on the amount of traffic my blog is currently receiving.

Moving posts to WordPress from Jekyll

As mentioned, I had nearly 900 posts on my blog. I had a handful of pages as well, but nothing that I felt I needed to migrate versus rebuilding it in WordPress. Also, my blog has always been light on images, so I didn’t bother to migrate any images automatically. This was the first of a small handful of concessions I made to get my content moved over without overthinking it.

Getting posts from Jekyll over to WordPress was accomplished with a custom XML feed from my current blog that was fed into the WordPress plugin WP All Import. I would have been happy to pay for WP All Import, but that wasn’t necessary based on my needs. Migrating posts, authors, categories and tags, even with nearly 900 posts, was able to done with the free version of WP All Import.

Custom XML file for WP All Import

There wasn’t anything fancy about the custom XML file that would compatible with WP All Import. I took my original RSS feed, and added a few additional bits of information that were then mapped within WP All Import.

I didn’t get the XML file right on the first try. In fact, it probably took a good half dozen redos before I was satisfied. Fortunately WP All Import is quite forgiving and allows for updating posts when you run subsequent imports.

Here’s the code for what ended up being the final import file:


<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="">
  {% for post in site.posts %}
      <title type="html">{{ post.title | xml_escape }}</title>
      <link href="{{ site.url }}{{ post.url }}" />
      {% assign slug_to_remove = post.url | slice: 0, 12 %}
      {% assign sliced_slug = post.slug | remove_first: slug_to_remove %}
      <slug>{{ sliced_slug }}</slug>
      {% if post.disqus %}
        <posted>{{ | date: "%Y-%m-%d %H:%M:30" | date_to_xmlschema }}</posted>
      {% else %}
        <posted>{{ | date: "%Y-%m-%d %H:%M:%S" | date_to_xmlschema }}</posted>
      {% endif %}
      <id>{{ site.url }}{{ }}</id>
      <summary type="html">{{ post.content | strip_html | truncatewords: 50 | xml_escape }}</summary>
      <content type="html">{{ post.content | xml_escape }}</content>
        {% if %}
          <name>Jenny Sherman</name>
        {% else %}
          <name>Josh Sherman</name>
        {% endif %}
      <category>{{ post.category }}</category>
      <tags>{{ post.tags | join: "," }}</tags>
  {% endfor %}

If your site only has a single author you could omit the <author> element entirely.

Content concessions moving to WordPress

I mentioned making a concession with regard to importing images from my Jekyll site into WordPress. While WP All Import handles importing images, I figured that since my blog isn’t image heavy, it would just be more work than it was worth.

The biggest concession I made, was that I wasn’t going to put a ton of time and effort into the formatting of the majority of posts post-import. Jekyll posts are markdown files, that I would edit in Vim. Most were wrapped to 80 characters. These line breaks were retained after the import.

WP All Import allows for the stripping of line breaks like this, but that resulted in my code snippets being condensed down to a single line. Great if you’re golfing your code, terrible for blog content. I would rather my code snippets look good than concern myself with a few stray line breaks.

Even with the line breaks retained in my code snippets, they were far from perfect, due to content overflowing the viewport. Another concession made, I simply added some additional CSS to the theme to make sure those elements had a scroll bar instead of overflowing. For the most part, this made Google Search Console a bit happier with my mobile usability.

The overarching theme of my migration efforts is that done is better than perfect. Considering that a small number of posts make up the majority of my blog’s traffic, it made more sense to focus on cleaning up those posts, which I did. Before flipping the switch on the site, I made sure my most trafficked posts were looking perfect. I even put a bit of effort into improving some of the SEO mojo of those posts.

Slug caveats from Jekyll to WordPress

With Jekyll, the URL slug is the entirety of the URL, including the date of the post. Because of this, I was able name my recurring posts the same, because the date was different. 2010-01-01-title-of-post and 2020-12-31-title-of-post are treating as wholly unique. I did this a lot and had 50+ posts that all had the same URL slug.

WordPress on the other hand, doesn’t care about the date in the URL slug, and expects the textual portion to be unique. WP All Import handles this well enough, appending a number to the end of the duplicate posts, -2, -3, -etc.

I didn’t want to mess up with existing links to those posts, so I updated the duplicate URL slugs to include the month and year of the post. This made sense for the posts with duplicate URL slugs as they were monthly posts. I then also setup a redirect with the -# URL to the -month-year URL.

There’s a handful of ways to create redirects, both inside of WordPress and externally at the DNS level. I opted for doing it within WordPress. The premium version of the Yoast SEO plugin not only handles redirects, but it also handles creating them automatically when you change the slug or delete a post.

Moving my theme to WordPress from Jekyll

Because I want to focus on my content, and not on the design aspect of my blog, I wanted to spend as little time and effort on my theme. Perhaps stubborn, I didn’t want to create my own theme or child theme. In fact, I don’t want to sling any PHP code for my blog, if possible.

With that, I bounced between a few ideas. As my blog’s look and feel was just a fairly minimal Bootstrap theme of my own design, around the Solarized color scheme, I checked Bootstrap WordPress theming options. Understrap looked pretty cool, but it was going to send me down the path of my own custom theme. Sadly, I wasn’t able to find a WordPress theme that used Bootstrap that would allow me to customize it the way I wanted to.

Where we started - Custom Solarized Bootstrap Theme - Powered by Jekyll and Cloudflaare Pages

I also checked out some of the “premium” themes that WP Engine offered, coincidentally the same “premium” themes that Flywheel offered. While they looked great, none of them really felt like aesthetic, not for my blog at least.

The other thing that always lingers in the back of my head is around ongoing support and improvements. I’d hate to be on a theme that eventually just craps out with a new version of WordPress. Also, in my experience, most of the themes I’ve used always feel heavy in comparison to what you get for free. The addition of the Gutenberg block editor has rendered these custom page designer systems completely unnecessary.

Sticking to the stock WordPress theme

So all this soul searching left me exactly where I started, with the stock WordPress theme, Twenty Twenty-Three. I was already using it on another site, and was quite happy with it. If nothing else, I trust that the official WordPress theme is going to be one of the best and lightest weight options available.

Not wanting my blog to look like my other site, I set out to customize the theme to match the Solarized color scheme and my desired font stack. I probably could have taken things a step further to match my site’s old header, but I’m not trying to win any awards for my WordPress design chops.

After I reached the point of satisfaction, I received a WordPress upgrade. Wouldn’t you know it, it included the newly minted Twenty Twenty-Four theme.

The new theme has to be better than last year’s model, so I went ahead and redid my efforts with the new theme and got back to my happy place with things.

Where we are - Solarized Twenty Twenty-Four - Powered by WordPress and WP Engine


Moving my blog’s posts to WordPress from Jekyll is really only the beginning of the story. The concessions I was willing to make were done so quite strategically as I knew that once things were moved over, I’d be putting a lot of effort into my existing content.

Some of that work has included pruning content, something I’ve talk about in a future post. The quality of the content I’ve created varies quite a bit. Some topics just aren’t good, while some posts were only relevant for a short time. Because some content just has to go, it didn’t make a lot of sense to pine over every little detail on every single post.

Google Search Console is doing a good job of alerting me of the things that are so messed up enough that I should deal with them. Fortunately, most of those issues aren’t post-specific, and can be resolved across all posts without much effort.

Sadly, I really wish I had stuck it out with WordPress earlier in the process. I have amassed a lot of content over the years, and I think the overall health of my blog would have been better if I wasn’t so stubborn about things.

Onward and upwards though. Now that the hard work of migrating is over, I can focus on the healing. Pruning, improving, and most importantly, focusing on higher quality posts, instead of vanity metric held together by thin content.

Josh Sherman - The Man, The Myth, The Avatar

About Josh

Husband. Father. Pug dad. Musician. Founder of Holiday API, Head of Engineering and Emoji Specialist at Mailshake, and author of the best damn Lorem Ipsum Library for PHP.

If you found this article helpful, please consider buying me a coffee.