Leaving WordPress After 9 Years: What Happened When I Moved My Blog to Ghost
I started my first blog on WordPress in 2015. By the time I migrated off it earlier this year, I had nine years of posts, a mailing list of about 14,000 subscribers, and a server bill that had quietly grown to $180/month for what was fundamentally a writing platform with a newsletter attached.
The migration wasn't impulsive. I'd been watching Ghost from a distance for two years, reading other writers' accounts, following the platform's development. What finally pushed me was a plugin conflict that corrupted my newsletter subscriber data during a routine update — 340 duplicate records, 89 unsubscribes that shouldn't have happened, and three hours of database cleanup on a Tuesday afternoon when I had a deadline.
At that point the cost-benefit math on WordPress — enormous flexibility I wasn't using, maintenance overhead I definitely was dealing with — flipped. I moved to Ghost. Here's the complete account, including the parts that didn't go smoothly.
Why Bloggers End Up Evaluating Ghost in the First Place
Before I get into the technical details, let me describe the profile of the person who should actually consider this move — because it's not everyone.
Ghost makes sense if most of these apply to you:
- Writing is the product, not a feature. Your site is primarily a publication. You're not running an e-commerce store, a portfolio, or a membership community with complex access rules.
- You have or want a newsletter as a primary channel. Ghost's newsletter functionality is built-in and genuinely good. It's not a plugin that sometimes works — it's the architecture the platform is built around.
- You're spending meaningful time on WordPress maintenance. Updates, plugin conflicts, security monitoring, database cleanup. If this is consuming hours per month, that time has a cost.
- You don't need the WordPress plugin ecosystem. This is the real tradeoff. WordPress has a plugin for everything. Ghost doesn't. If you need specific integrations, you'll build them yourself or use Zapier.
- You want fast, clean page loads without configuring a caching stack. Ghost's default performance, out of the box, is better than a typical WordPress install before optimization.
If you're running a complex membership site, selling digital products, or managing contributors across a large publication, Ghost's feature set may not be deep enough. Know what you actually need before evaluating.
The Migration: What WordPress-to-Ghost Actually Looks Like
Ghost has an official WordPress export importer, and it works reasonably well for the content itself. The process:
Step 1: Export from WordPress
Install the Ghost Export plugin on your WordPress site. It generates a JSON file containing your posts, pages, tags, authors, and basic metadata. It does not export: comments (Ghost doesn't have native comments), custom fields from Advanced Custom Fields or similar plugins, WooCommerce data, or complex shortcode-based layouts.
For my site — primarily long-form articles with minimal shortcode usage — the export worked cleanly. If your content is heavily dependent on page builder shortcodes, expect manual cleanup work.
Step 2: Import into Ghost
In Ghost Admin, under Settings > Labs > Import content, upload the JSON file. Ghost processes it and creates drafts for each imported post. Review them — a small percentage of posts will have formatting issues from the conversion, particularly around code blocks and custom HTML.
Step 3: Image migration
Here's where it gets annoying. Ghost's importer doesn't pull your WordPress media library. Images in imported posts still reference your old WordPress domain. You have two options:
Option A: Ghost's bulk image import tool
# Ghost CLI — migrate images from old domain
ghost migrate wordpress --url https://your-old-wordpress-site.com
This crawls the imported content, finds external image references, downloads them, re-uploads to Ghost's storage, and updates the references in the database. It works, but takes time proportional to your image library size.
Option B: Keep images on the old domain and redirect
If your old WordPress hosting is staying up, you can keep the image URLs pointing there and just ensure the domain serves those files indefinitely. Less clean, but faster to implement.
I used Option A. 1,847 images across nine years of posts. The migration process ran overnight and handled 94% of them automatically. The remaining 6% were images that had been deleted from WordPress media library at some point but were still referenced in post content — I found and fixed those manually over a few days.
Handlebars Templating: The Learning Curve Ghost Doesn't Advertise Well
Ghost themes use Handlebars.js templating instead of PHP. If you're coming from WordPress theme development, this requires a mental shift.
In WordPress, a blog listing page might look like:
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h2><?php the_title(); ?></h2>
<p><?php the_excerpt(); ?></p>
<?php endwhile; endif; ?>
In Ghost/Handlebars, the equivalent:
{{#foreach posts}}
<h2>{{title}}</h2>
<p>{{excerpt}}</p>
{{/foreach}}
Simpler syntax. Less flexible than PHP for complex conditional logic. Ghost's template system handles the common blogging use cases well — post lists, single post views, tag archives, author pages — and gets awkward when you need something outside those patterns.
The theme I settled on for my own setup, after testing four options, was the Zimac Ghost Theme. The reason was specifically how it handles long-form reading. The typography is tuned for articles that run 2,000-4,000 words — comfortable line height, appropriate measure (line length), and a table of contents implementation for longer posts that doesn't require a plugin. On Ghost, anything that would have required a plugin on WordPress either needs to be in the theme or built with the Content API. A theme that has these things by design is worth choosing for a writer.
The newsletter subscription form placement in Zimac is also thoughtful — it appears at the end of every post, after the reader has finished, rather than interrupting reading with a popup. That placement converts better for writing-focused audiences because the person who reaches the end of a long post is demonstrating real engagement. That's when to ask for the subscription.
The Ghost Content API: Building What You Lose From WordPress Plugins
When I moved to Ghost, I lost the WordPress plugin ecosystem. Some things I'd relied on needed to be rebuilt. The Ghost Content API made this possible for most use cases — it's a read-only REST API that lets you pull your Ghost content into other systems.
Reading list integration
I maintain a curated weekly reading list as a separate newsletter section. On WordPress, I'd used a plugin that let me save links and auto-include them in a newsletter digest. On Ghost, I built a lightweight integration using the Content API:
// Node.js script — runs weekly via cron
const GhostContentAPI = require('@tryghost/content-api');
const api = new GhostContentAPI({
url: 'https://yourblog.com',
key: 'your_content_api_key',
version: 'v5.0'
});
async function getWeeklyPosts() {
const oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
const posts = await api.posts.browse({
filter: `published_at:>='${oneWeekAgo.toISOString()}'`,
fields: 'title,url,excerpt,published_at',
order: 'published_at desc',
limit: 10
});
return posts;
}
async function generateDigest() {
const posts = await getWeeklyPosts();
// Format for Mailchimp or send via Ghost's newsletter system
const digestContent = posts.map(post =>
`## [${post.title}](${post.url})\n\n${post.excerpt}\n\n`
).join('---\n\n');
console.log(digestContent);
// Pass to your newsletter system of choice
}
generateDigest();
This runs every Sunday, pulls the week's published posts, and formats them for the weekly digest email. It took me about two hours to write and has worked without issue since.
Search functionality
Ghost doesn't have native search. The typical solution is Algolia's DocSearch or a simpler client-side search using the Content API to pre-build a search index. I went with the client-side approach:
// Build search index from Ghost Content API
async function buildSearchIndex() {
const api = new GhostContentAPI({
url: process.env.GHOST_URL,
key: process.env.GHOST_API_KEY,
version: 'v5.0'
});
const allPosts = await api.posts.browse({
limit: 'all',
fields: 'title,url,excerpt,tags,published_at'
});
// Write to a JSON file served as a static asset
const fs = require('fs');
fs.writeFileSync(
'./public/search-index.json',
JSON.stringify(allPosts)
);
console.log(`Search index built: ${allPosts.length} posts`);
}
This runs on each deploy, generating a static JSON file. A small client-side script (FlexSearch.js, about 6KB) handles the search interface. For a site with a few hundred to a few thousand posts, this scales fine. For larger archives, Algolia becomes necessary.
RSS-to-Email Pipeline: Ghost's Newsletter Layer
Ghost's newsletter system is one of the strongest reasons to move to it for writers. It's not a plugin — it's the platform. Every email goes out through Ghost's built-in sending system (powered by Mailgun), subscribers are managed natively, and the stats (open rates, click rates) are in the same dashboard as your content.
My setup before the migration: Ghost → Mailchimp → subscribers, via an RSS-to-email campaign. Fragile. Multiple moving parts. Delayed by up to 24 hours after publish.
After migration: Ghost sends the newsletter directly on publish, or on a scheduled send time I set per post. No RSS polling delay, no external service dependency.
The Ghost newsletter configuration that I adjusted from defaults:
In Settings > Newsletter:
- Default send time: I disabled immediate send and switched to "specific time" — I schedule each newsletter for 7:00am in the reader's local timezone (Ghost estimates this based on subscriber timezone data).
- Sender name: My name, not the publication name. Open rates are higher when the from-name is a person.
- Reply-to: My actual email address, not a no-reply. This is a deliberate decision — readers who reply to newsletters are a disproportionately engaged segment, and I want those conversations in my inbox.
- Track opens and clicks: On, with full awareness that iOS Mail's Privacy Protection affects open rate accuracy since iOS 15.
The technical piece I added: a Ghost webhook that fires on post publish and sends a notification to a Slack channel, so I can confirm sends went through without logging into Ghost Admin to check:
{
"name": "Newsletter Send Notification",
"event": "post.published",
"target_url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
"active": true
}
Performance Comparison: Real Numbers
I measured both setups under comparable conditions — same content, same approximate traffic level.
WordPress (before migration):
- Hosting: $180/month managed WordPress hosting
- Lighthouse mobile: 68 (with WP Rocket + Cloudflare configured)
- LCP: 2.8s
- TTFB: 380ms
- Monthly maintenance time: ~4-5 hours
Ghost (after migration):
- Hosting: $25/month Ghost Pro (starter plan, self-managed alternative is ~$10/month on DigitalOcean)
- Lighthouse mobile: 94 (default configuration, no optimization stack needed)
- LCP: 1.2s
- TTFB: 140ms
- Monthly maintenance time: ~30 minutes
The performance difference is partly platform and partly architecture — Ghost serves static-ish content with far less PHP overhead than a WordPress install, and Ghost Pro's infrastructure is tuned for this use case. The $155/month cost reduction was the clearest financial argument for the move.
Maintenance time dropping from 4-5 hours to 30 minutes/month is harder to put a dollar figure on but is the quality-of-life improvement I feel most acutely. Those were hours spent on infrastructure. Now they go to writing.
What I Genuinely Miss About WordPress
I want to be honest about the tradeoffs because migration posts that only talk about what's better are unhelpful.
I miss the plugin ecosystem. Specifically:
- Table of contents generation — Ghost themes can include this but not all do, and theme-level implementation is less flexible than a dedicated plugin. Zimac handles this correctly; it's a genuine differentiator in my theme selection.
- Advanced redirection management — Redirection plugin on WordPress is excellent. Ghost's URL redirect management is basic by comparison. I manage complex redirects at the Nginx level now, which is fine but requires more technical knowledge.
- SEO tooling depth — Rank Math and Yoast have deep technical SEO features: schema customization, breadcrumb configuration, internal linking suggestions. Ghost's SEO panel is adequate for basics but doesn't match the depth.
I miss comments. Ghost doesn't have native comments, and third-party solutions (Cove, Disqus) feel bolted-on in a way that WordPress's native comment system never did. For my specific readership, email replies to newsletters fill some of that function, but it's not the same.
Multiauthor publishing is workable but not elegant. For a solo writer it's fine. For a publication with a team, the workflow in Ghost is simpler than WordPress's granular role system.
None of these made me regret the move. They're real limitations worth knowing before you make it.
Sourcing Themes for Ghost Blogs
Ghost theme options are a smaller market than WordPress — that's the honest reality. For writers looking for options in the personal blog and minimalist magazine space, the pool of quality themes is narrower, which makes evaluating each one more important.
Beyond the theme, for developers managing multiple Ghost implementations or looking for WordPress-side resources as part of a hybrid setup, browsing a WooCommerce Themes Collection gives a useful contrast for understanding what WordPress themes in comparable niches look like. For WordPress plugin components that might be part of a migration workflow (redirect management, content export, subscriber list tools), Premium WordPress Plugins is a practical sourcing reference.
The usual note: GPL redistribution sources provide theme and plugin files under the appropriate license. For active, ongoing use where you need support from the original developer, buying direct keeps that relationship intact.
The Bottom Line After Eight Months on Ghost
My newsletter open rate went from 28% to 41%. I attribute most of that to the improved delivery infrastructure and the simpler, faster email template (no plugin-generated HTML cruft). Some of it is probably just reader behavior in a different sending cadence.
My writing output went up. Not dramatically — I'm not going to claim the platform change made me a better writer. But removing the maintenance overhead and the occasional plugin crisis freed up cognitive space that I previously spent on infrastructure and redirected toward the actual work.
Would I recommend it to every blogger? No. If your WordPress setup works well, the plugin ecosystem is serving your needs, and you're not fighting the maintenance overhead, stay. Migrations have real costs — redirects to manage, SEO turbulence during transition, time to rebuild integrations.
If you're in the situation I was in — where the platform is a drag on the practice rather than an enabler of it — the move is worth making, and worth making carefully.
The WordPress documentation on content migration, export formats, and REST API content endpoints — relevant for anyone planning a platform transition that preserves content structure — is maintained at the WordPress Developer Handbook, and is the reference I used for understanding exactly what the Ghost exporter was pulling from WordPress's data model before I ran it on production.



