
How I migrated my personal website from WordPress on a Polish hosting to an open-source AstroJS Starlight site on Cloudflare Pages — and why it was worth every commit.
Before (WordPress with 25 plugins):

After (Astro Starlight, open-source):

The site is no longer just a portfolio and blog — it’s now a digital garden with a knowledge base section. It starts with just a handful of pages, but I plan to expand and regularly update them over time. I wanted it to feel closer to my personal Obsidian vault: a place where notes evolve, interlink freely, and stay in plain Markdown. The sidebar, dark theme, and “Last updated” timestamps all echo that Obsidian aesthetic.
Why migrate?
Section titled “Why migrate?”My WordPress site had been running for 8 years (since 2018) on a Polish shared hosting provider (MyDevil). It served me well, but over time I started feeling the friction:
- 25 plugins to maintain — security updates, compatibility issues, bloat (some were paid)
- Subscriptions for updates — my theme and some plugins required yearly subscriptions to keep receiving updates
- No version control — changes were irreversible clicks in a web admin panel
- Slow performance — shared hosting with PHP overhead
- Vendor lock-in — content trapped in a database, not in files I own
- PHP version dictated by hosting — limited control over the runtime environment
- Cost — yearly hosting fees for what is essentially a static site
I wanted something that would give me full control over my content as plain Markdown files, version history through Git, free hosting, and lightning-fast performance. Part of what motivated me was my move from OneNote to Obsidian back in 2022. Since then, I’ve been writing and editing Markdown daily, both for personal notes and at work. WordPress felt increasingly out of place.
After some research, I landed on AstroJS Starlight deployed on Cloudflare Pages, with the source code open on GitHub. Since it’s open-source, I welcome collaborative suggestions — just please, no ads or tool promotions.
Choosing a framework
Section titled “Choosing a framework”Before committing, I evaluated several static site generators and themes:
- Hugo — fast and popular, doesn’t require JavaScript knowledge. Webmastah’s migration from WordPress to Hugo back in 2021 planted the seed in my head. I looked at themes like PaperMod, Stack, and PaperModX. There’s also good tooling for Obsidian-to-Hugo and a WordPress to Hugo Exporter plugin.
- GatsbyJS — used by Tania Rascia and Victor Zhou, both of whom I’ve followed since starting this blog.
- VitePress — used by Krystian Kościelniak for his digital garden, which inspired me the most. We even exchanged a few messages on Instagram about his setup.
- AstroJS Starlight — ultimately my pick. Reddit recommendations at the time pointed strongly toward Astro, and I noticed many documentation-based websites were adopting it. It’s built on Astro, has an excellent sidebar and search out of the box, supports Markdown/MDX, and the showcase demonstrated it could handle a blog + digital garden (knowledge) base setup well. I didn’t find many sites combining traditional blogging with a knowledge base in this way, but I believe both systems complement each other nicely.
Part of what drew me to a documentation-style framework is that I genuinely enjoy writing and maintaining documentation at work. Starlight’s structure felt natural.
For hosting, Cloudflare Pages stood out as likely the best free option over Netlify and Vercel — generous limits, global edge network, and tight DNS integration.
Inspirations
Section titled “Inspirations”Before diving in, I studied how others had built their personal sites, especially:
Each had a different approach, but the common thread was: static site, Markdown content (mostly, I believe), Git-backed, fast.
The migration process
Section titled “The migration process”Step 1: Set up the basics
Section titled “Step 1: Set up the basics”Before anything else, I installed Node.js (I use nvm to manage versions) and created a GitHub repository for the project. This gives you version control from day one.
Step 2: Export WordPress content
Section titled “Step 2: Export WordPress content”WordPress has a built-in export tool under Tools → Export. I exported all content as an XML file, then used wordpress-export-to-markdown to convert it:
npm install -g wordpress-export-to-markdownwordpress-export-to-markdown --input=export.xml --output=src/content/docsOptions I chose:
- Put each post into its own folder? → No
- Add date prefix to posts? → Yes
- Organize posts into date folders? → No
- Save images? → All Images
This gave me all my posts as .md files with frontmatter and images — a solid starting point. Unfortunately, this was just the beginning. The exported content required significant cleanup:
- Converting WordPress shortcodes to standard Markdown links
- Replacing remote
wp-contentimage URLs with local assets - Standardising ~284 image filenames (lowercase, hyphens) for Linux case-sensitivity
- Fixing quote formatting, highlight blocks, and image captions
- Removing outdated pages and unused frontmatter fields
This turned into a massive PR that also added site infrastructure (RSS, LaTeX support, custom components) and fixed ~30 grammar issues across 11 files.
Step 3: Set up Starlight
Section titled “Step 3: Set up Starlight”Creating the Starlight project was straightforward:
npm create astro@latest -- --template starlightThe template gives you a working site out of the box with a sidebar, search (via Pagefind), dark/light theme toggle, and sensible defaults. From there, I customised the sidebar structure, added my branding, and dropped in the exported content.
Step 4: Chat with Claude Code
Section titled “Step 4: Chat with Claude Code”Here’s where it gets interesting. I used the Claude Code extension in VS Code (which later became GitHub Copilot with Claude) to assist with the bulk of the migration work. Having an AI pair programmer turned what could have been weeks of tedious work into a much more manageable process.
Claude helped with:
- Fixing broken links and WordPress leftovers across all posts
- Adding SEO infrastructure (Schema.org structured data, Open Graph tags,
robots.txt) - Building custom components (page title with reading time, footer with JSON-LD)
- Setting up tag pages and RSS feed
- Creating URL redirects from old WordPress paths
- Image optimisation and deduplication
- Grammar review across all content
The full commit history on GitHub tells the story.
Step 5: Deploy to Cloudflare Pages
Section titled “Step 5: Deploy to Cloudflare Pages”Deploying to Cloudflare Pages was remarkably simple:
- Connected the GitHub repository in the Cloudflare dashboard
- Set the build command to
git fetch --unshallow && npm run build- The
git fetch --unshallowis important — Cloudflare Pages does a shallow clone by default, which breaks Starlight’s “Last updated” dates (every page shows the deployment date instead of its actual last-modified date)
- The
- Set
NODE_VERSIONto22and build output todist - Pointed my domain’s DNS to Cloudflare
That’s it. Every push to main triggers an automatic deployment. Every branch and commit gets its own preview URL, which makes testing changes much easier before merging. Rollbacks are one click away.
┌─────────┐ git push ┌─────────┐ webhook ┌──────────────────┐│ VS Code │ ───────────────► │ GitHub │ ──────────────► │ Cloudflare Pages │└─────────┘ └─────────┘ └────────┬─────────┘ │ npm run build │ ▼┌──────────┐ CDN ┌──────────────┐ static ┌─────────────────┐│ Visitors │ ◄────────────── │ Edge Network │ ◄──────── │ Astro SSG │└──────────┘ └──────────────┘ └─────────────────┘Step 6: Polish and enhance
Section titled “Step 6: Polish and enhance”After the core migration, I spent time on improvements that would have been painful or impossible on WordPress:
- Custom 404 page with a themed dead link illustration
- Tag system with an index page and individual tag pages
- “Discuss on” links per post (linking to Facebook, X, LinkedIn discussions)
- Responsive iframe wrapper for YouTube embeds via a custom remark plugin
- Mobile header auto-hide on scroll via custom JavaScript and CSS
- i18n customization to rename Starlight’s “On this page” heading
- Broken link checker script (
npm run check:links) — scans all Markdown files for URLs, then checks them concurrently via HTTP HEAD/GET and reports dead links grouped by file - Recommendations sync script (
npm run sync:recommendations) — pulls the recommendations-for-engineers README from GitHub and transforms it into a Starlight-compatible page (converting admonitions, stripping TOC, fixing links) - Newsletter migration from Mailchimp to Substack
- Donation link migration from PayPal to Ko-Fi
- Yearly auto-rebuild via a Cloudflare deploy hook triggered by GitHub Actions (to keep the copyright year current)
- Downtime monitoring with UptimeRobot — free checks every 5 minutes with email alerts
- Knowledge base section — a growing digital garden with 16+ pages on topics from Kubernetes to music production
Auditing WordPress plugins
Section titled “Auditing WordPress plugins”One concern before migrating was: what functionality would I lose? I went through all 25 WordPress plugins to check:
| Plugin | Needed? |
|---|---|
| Akismet Anti-spam | ❌ No comments system (yet) |
| Classic Editor / Gutenberg | ❌ Writing in Markdown now |
| Custom Highlight Color | ❌ Handled by CSS |
| Enlighter - Syntax Highlighter | ❌ Starlight uses Shiki with excellent syntax highlighting |
| Fixed TOC | ❌ Starlight has built-in table of contents |
| footnotes | ❌ Standard Markdown footnotes work |
| Forms for Mailchimp | ❌ Migrated to Substack |
| instant.page | ❌ Static site is already fast |
| Jetpack | ❌ Cloudflare provides analytics |
| Phoenix Media Rename | ❌ Files are just files in a Git repo |
| Post Reading Time Estimate | ❌ Custom PageTitle component calculates this |
| Redirection | ✅ Reimplemented as Astro redirects in config |
| WP-KaTeX | ❌ Using remark-math + rehype-katex |
| Yoast SEO | ❌ Custom Schema.org + Open Graph implementation |
| …and 11 others | ❌ |
Only Redirection required actual reimplementation — and it was just a config object in astro.config.mjs mapping old WordPress date-based URLs to new paths.
The results
Section titled “The results”Performance
Section titled “Performance”I ran Lighthouse audits on both sites to compare. The results were dramatic:

And after the migration:

The difference in Lighthouse scores speaks for itself. The WordPress site on shared hosting struggled with performance, while the Astro static site on Cloudflare’s edge network scores near-perfect across the board.
The accessibility score of 98/100 is intentionally not 100 — on some pages I use non-sequential heading levels (e.g. starting from ### instead of ##) to achieve a smaller font size in certain sections.
Current setup
Section titled “Current setup”| Service | Provider | Cost |
|---|---|---|
| Website | Astro (hosted on Cloudflare Pages) | Free |
| Domain DNS | Cloudflare | Free |
| Newsletter | Substack | Free |
| Small.pl | ~50 PLN/year |
What I gained
Section titled “What I gained”- Full version control — every change is a Git commit with history. I try to follow the Conventional Commits format for clarity. Visitors can also browse the commit history to see what’s changed
- Open source — the entire site is on GitHub
- Nearly free — only ~50 PLN/year for email, everything else is free
- Speed — static HTML served from edge locations worldwide
- Content as files — Markdown files I own, not rows in a database
- Knowledge base — Starlight’s sidebar makes it easy to organise a growing digital garden
- Work from anywhere — just clone the repo on any device (macOS, Linux, Windows all work fine), run
npm run dev, and deploy with a push. No FTP, no hosting panel, no database credentials - LLM-friendly codebase — AI coding assistants like Copilot and Claude can read and modify the entire site. In WordPress, LLMs struggled with the PHP/database split and the admin-panel workflow
What I lost
Section titled “What I lost”- Comments — WordPress had built-in comments. I haven’t considered adding a comments section yet, but feedback is welcome via GitHub issues or in the linked social media posts at the bottom of each blog post
- Potentially some visitors during migration — there was a brief DNS transition period
- WYSIWYG editing — but I much prefer writing in Markdown anyway
Tips if you’re considering the same
Section titled “Tips if you’re considering the same”- Export early, clean later — get the content out of WordPress first, then iterate on it in your new setup
- Use
git fetch --unshallowon Cloudflare Pages — otherwise “Last updated” dates will be wrong - Set up redirects from your old URL structure — don’t break existing links from Google and other sites
- Leverage AI for the tedious parts — link fixing, frontmatter generation, grammar review
- Don’t aim for perfection on day one — ship it, then improve incrementally
What’s next?
Section titled “What’s next?”The site is live and I’m happy with the result. The knowledge base is growing, and writing new content is a joy compared to the WordPress admin panel. If you’re running a static-ish blog on WordPress and feeling the friction — I encourage you to make the jump. The tooling in 2026 makes it easier than ever.
The source code is at github.com/pyxelr/pawelcislo.com — feel free to explore for inspiration or open an issue if you have questions.