How do I migrate from Ghost to VeloCMS?
Export your Ghost content as JSON, import it into VeloCMS, preserve members and paid subscriptions, reapply themes, and configure 301 redirects — the complete playbook.
Ghost is a solid CMS, but you're here because something's not working — maybe the pricing jumped, maybe you need a feature Ghost doesn't offer, maybe you just want to own the stack. Whatever the reason, migrating to VeloCMS is straightforward if you follow the steps in order. This guide covers everything: content export, member import, Stripe reconnection, theme reapplication, and finally the DNS and redirect setup that protects your search rankings.
Step 1 — Export your Ghost content as JSON
In your Ghost admin panel, go to Settings → Labs → Export your content. Ghost generates a single JSON file (something like ghost-export-2026-05-11.json) that contains all your posts, pages, tags, and authors. This file can be anywhere from a few kilobytes for a fresh blog to several hundred megabytes if you've been publishing for years. Ghost does NOT include member emails in this export — that's a separate step below.
Ghost export does not include images. Your post body references image URLs on your old Ghost domain. Before you cut over DNS, run a find-and-replace on image URLs so they point to your new VeloCMS R2 storage bucket or leave them pointing at the old domain temporarily while you upload images manually.
Step 2 — Import the JSON into VeloCMS
Log in to VeloCMS admin, go to Admin → Tools → Import, and upload the Ghost JSON file. VeloCMS maps Ghost's post fields to the VeloCMS post model: title, slug, HTML body, published_at, and tags all carry over cleanly. Custom post templates and card styles are not preserved — those are theme-specific and need to be reapplied via the VeloCMS theme settings after import.
- Posts with status 'published' import as published.
- Draft posts import as drafts.
- Scheduled posts import as drafts with the original scheduled date in the meta — you'll need to reschedule them manually.
- Tags become categories in VeloCMS.
- Authors map to free-text author names on each post (not VeloCMS user accounts).
Step 3 — Migrate Ghost members
In Ghost admin, go to Members → Export members. You'll get a CSV with columns like email, name, subscribed_at, stripe_customer_id, labels, and complimentary_plan. In VeloCMS admin, go to Admin → Members → Import CSV. The importer expects email, name, subscribed_at as minimum columns. If you have paid members, see Step 4 before importing — you'll want to reconnect Stripe first so paid member status carries over correctly.
Step 4 — Reconnect Stripe for paid members
If you have paying Ghost members, they were billed through Ghost's Stripe integration, which means their subscriptions live in your Stripe account. VeloCMS BYOK (Bring Your Own Keys) lets you connect the same Stripe account. Go to Admin → Settings → Membership → Connect Stripe and paste your Stripe secret key. Once connected, VeloCMS can look up active subscribers by email and restore their paid status automatically during the CSV member import — as long as their Stripe subscription is still active.
If a member cancelled on Ghost, their Stripe subscription is inactive. VeloCMS imports them as free members regardless. You can't restore a cancelled subscription — they need to re-subscribe at your new VeloCMS price.
Step 5 — Reapply your theme
Ghost themes (Handlebars .hbs) don't run on VeloCMS — VeloCMS uses a preset-based theme system built on React and Tailwind. Head to Admin → Themes and pick the preset that most closely matches your old Ghost design. Then customise colors, typography, and layout in the Theme editor. If your Ghost theme had a very custom look, this step takes the longest — budget 30–60 minutes.
Step 6 — Set up 301 redirects
Ghost uses URL patterns like yourdomain.com/post-slug. VeloCMS uses the same pattern by default, so most links carry over with no redirect needed. However, if your Ghost blog was running on a subdomain (e.g. blog.yourdomain.com) and you're moving to a bare domain (yourdomain.com), you'll need a 301 at the DNS or reverse-proxy level. In VeloCMS, go to Admin → Settings → Redirects and add rules for any URL pattern mismatches.
# Example Nginx rule if you moved from blog.yourdomain.com to yourdomain.com
server {
server_name blog.yourdomain.com;
return 301 https://yourdomain.com$request_uri;
}Step 7 — Update your Cloudflare or DNS settings
Once content is imported, themes are set, and redirects are configured, update your DNS to point to VeloCMS. Go to Admin → Custom Domains → Add Domain, verify your domain, then update the CNAME record in your DNS provider (Cloudflare, Namecheap, etc.) to point to your VeloCMS subdomain like yourname.velocms.org. SSL is provisioned automatically within a few minutes of DNS propagation.
Step 8 — Post-migration checks
- Open 5–10 old Ghost post URLs and confirm they load on your new domain or redirect cleanly.
- Submit your updated sitemap in Google Search Console: Admin → SEO → Sitemap URL → copy and paste into GSC.
- Send a test email to yourself via Admin → Members → Send Test Newsletter to confirm member email delivery works.
- Log in as a paid member to confirm the paywall renders correctly.
- Check your Plausible or analytics dashboard the day after cutover to confirm traffic is flowing to the new domain.
Frequently asked questions
- Can I keep my Ghost site live during migration? Yes — run both in parallel until you cut over DNS. Ghost keeps serving the old URLs while you build VeloCMS.
- Does VeloCMS support Ghost's native newsletter delivery? VeloCMS uses Resend for email delivery, which is similar but separate from Ghost's built-in Mailgun integration.
- What about Ghost's portal UI? VeloCMS has its own member portal at /member — it's similar in function (subscribe, manage account, sign in).
- Can I migrate Ghost pages (not posts)? Yes — Ghost 'pages' import as VeloCMS pages with the 'page' post type.
- What's the hardest part of Ghost migration? Image re-hosting. Ghost stores images on its own CDN; VeloCMS needs you to re-upload them to R2 or update the URLs in post content.