This is a continuation of the article on automating ProHomeLab publishing (Obsidian → Forgejo → Hugo). If you haven’t set up this pipeline yet, read that article first - here I’ll be building on it as an established foundation.
If automatic publishing via Forgejo Actions is already working for you, the next logical step is to think about where the site is physically hosted. I kept ProHomeLab on shared hosting1, but decided to move to GitHub Pages: it’s free, reliable, and takes the burden of server maintenance off my shoulders.
The problem is that “just pick up and move” is a bad idea for a site that’s already indexed. One wrong move with DNS or the domain, and you can lose your Google and Yandex rankings for weeks while the search engines figure out what happened to the site. In this article I’ll walk through, step by step, how to migrate hosting so that nothing changes for readers or search engines - except that the site is now served from GitHub’s servers.
What we have to start with#
- The domain
prohomelab.com, DNS already moved to Cloudflare, mode set to DNS only (grey cloud, no Cloudflare proxying). This matters: if the Cloudflare proxy were enabled, GitHub wouldn’t be able to validate the domain and issue a certificate. - A working pipeline: you write an article in Obsidian → Obsidian Git commits and pushes to Forgejo (
prohomelab-content) →workflow_dispatchfires → on a self-built runner (an LXC container in Proxmox, Hugo with the Blowfish theme)hugo -Druns → the resultingpublic/is uploaded via FTP to the old host. - We’re changing only the last step - instead of uploading via FTP, the built site will be pushed to a repository on GitHub, from which GitHub Pages serves it. Everything else - Obsidian, Forgejo, the runner, the Hugo build - stays as is.
Why not do it “properly” right away (build via GitHub Actions, Forgejo as a plain mirror)? Because that’s a separate, larger architectural overhaul, and doing it at the same time as the hosting migration is an unnecessary risk. First stabilize the hosting, then, without rushing, simplify the build.
Spoiler
Things ended up looking nothing like I originally pictured them - but you’ll read about that in later articles.
Why this shouldn’t break SEO at all#
The most important thing to understand: we are not changing the domain and not changing the URL structure. For Google and Yandex this isn’t a “site migration” in the sense they’re wary of - redirects, lost link equity, weeks of traffic drops. From a search engine’s point of view, prohomelab.com served the same article URLs before and will keep serving them - the only difference is that the request now reaches GitHub’s servers instead of the old host’s. This is invisible to the crawler as long as we don’t mess it up ourselves. And you can mess it up in these ways:
- Changing the link structure. If Hugo’s permalinks suddenly change (say, a trailing slash appears or disappears, or the format changes from
/posts/slug/to/slug/), the old indexed pages turn into 404s, and that’s when real problems start. - Leaving the site without HTTPS for a long time, or with a “broken” certificate. Google demotes sites with security issues in rankings, and browsers scare users off.
- A long outage during the DNS switch. If the site is “down” for a day, it’s not a catastrophe, but it’s best to keep the downtime window as short as possible.
- Silently losing Google Search Console / Yandex.Webmaster verification. More on this below - a separate, important point that’s easy to miss precisely because it isn’t directly tied to GitHub.
Yours truly, through sheer carelessness and a lack of prior experience, tried out mistake #1 firsthand - back when the site ran on an Astro theme for half a year. Trust me, you don’t want that.
Below are the concrete steps that account for all of this.
Step 1. Create a repository on GitHub#
Go to GitHub and create a new public repository - for example, prohomelab-pages. It has to be public because free GitHub Pages on a personal account serves sites from public repositories (or you need a private repository with a special workflow using actions/deploy-pages, but that’s Phase 2 with GitHub Actions, not now).
The repository name doesn’t need to match your-username.github.io - since we’re using our own domain, that restriction doesn’t apply.
Step 2. Make sure Hugo won’t change the link structure#
This is the single most SEO-critical step in the whole article, so don’t skip it.
Open the Hugo config (hugo.toml or config.yaml at the root of the Blowfish project) and check:
baseURL- must stayhttps://prohomelab.com/, unchanged;permalinks(if set for the posts section) - must be exactly the same as when the site was built for the old host;uglyURLs- must stay in the same state (false, most likely, given you have URLs like/posts/slug/rather than/posts/slug.html).
Since we’re using the exact same Hugo project as before and only changing the deployment target, nothing should change here by default. But it needs to be checked by hand, because if this is the one thing that goes wrong, it’s exactly what will hit your search rankings hardest.
Step 3. Create a token for pushing from Forgejo to GitHub#
Forgejo Actions needs to be able to push to your new repository on GitHub - for that you need a token.
- On GitHub: Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token.
- Repository access - choose Only select repositories and pick the
prohomelab-pagesrepo you just created. - Permissions → Repository permissions → Contents: Read and write. Don’t grant anything else - the token should be able to do exactly what’s needed, and nothing more.
- Generate the token and copy it immediately - GitHub won’t show it a second time.
Next, go to Forgejo, into the prohomelab-site repository → Settings → Actions → Secrets and add a new secret, e.g. GH_PAGES_TOKEN, with the token value.
Step 4. Add a CNAME file so the domain doesn’t get lost on every build#
GitHub Pages learns the site’s custom domain from a CNAME file at the repository root. Since we’ll be pushing a fresh public/ build every time (overwriting the repository), this file needs to be generated along with the rest of the site - otherwise it will be lost after the very first deploy.
Create a static/CNAME file in the Hugo project (where Blowfish lives) with a single line:
prohomelab.comHugo automatically copies everything from static/ into public/ on every build, so the file will end up in the deploy on its own, with no extra steps.
Step 5. Change the last step in deploy.yml#
Open deploy.yml in the prohomelab-site repository on Forgejo. Find the step that uploads public/ via FTP to the old host and replace it with a push to GitHub. The logic of the step:
- name: Deploy to GitHub Pages
run: |
cd public
git init
git checkout -b main
git add -A
git -c user.name="prohomelab-bot" -c user.email="bot@prohomelab.com" commit -m "Deploy: ${{ gitea.sha }}"
git push --force https://x-access-token:${{ secrets.GH_PAGES_TOKEN }}@github.com/your-username/prohomelab-pages.git mainReplace your-username with your actual GitHub login. Don’t touch the Sync content and Build with Hugo steps before this one - they keep working exactly as before, only the destination of the finished build changes.
Save and commit the changes to prohomelab-site. Don’t run it yet - first you need to enable Pages on the GitHub side (the next step), otherwise there’s nowhere to push to for checking the result.
Step 6. Enable GitHub Pages and verify the build WITHOUT switching the domain#
This is the key technique for a safe migration: we fully verify that the site builds and works correctly on GitHub before touching DNS. That way we always have a working fallback to the old host if something goes wrong.
- In the
prohomelab-pagesrepository → Settings → Pages. - Source: Deploy from a branch.
- Branch:
main, folder/ (root). - Don’t touch the custom domain yet - leave it blank.
- Save.
Now manually trigger the deploy.yml workflow in Forgejo (via the UI button or the workflow_dispatch API you’re already using). A minute or two after the first push, GitHub will build Pages, and the site will become available at an address like:
https://your-username.github.io/prohomelab-pages/Open this address. Browse a few articles, check that images, styles, and internal links are in place. Don’t worry if something looks slightly off from what you’d expect (for example, because of the baseURL hardcoded to prohomelab.com, some absolute links may point to the wrong place) - that’s expected on this intermediate address and doesn’t mean it will look the same after the domain switch. The main goal at this step is to confirm the build itself completes without errors and the content is in place.
Step 7. Make sure you won’t lose verification in Search Console and Yandex.Webmaster#
This is the one point that’s often overlooked because it’s not formally tied to GitHub at all - but it is tied to the domain having recently moved to Cloudflare.
Site verification in Google Search Console and Yandex.Webmaster is most often done one of these ways:
- via a TXT record in the domain’s DNS (the most common, and the most “fragile” during a DNS move);
- via a meta tag on the homepage;
- via a file on the server.
If your verification was via a TXT record, and you recently migrated the DNS zone to Cloudflare, there’s a chance that record didn’t come along with the rest. Check:
- Go into Cloudflare → DNS → Records for
prohomelab.comand look for TXT records likegoogle-site-verification=...or similar for Yandex. - If they’re missing, go into Google Search Console and Yandex.Webmaster and check the site’s verification status. If it’s dropped, just add the current TXT record again in Cloudflare (the services will show you the exact value in the verification UI).
Do this now, before switching to GitHub, not after - so that by the time real monitoring starts post-migration, access to data in both dashboards definitely works.
While you’re at it, check robots.txt (https://prohomelab.com/robots.txt) for an accidental Disallow: /, and sitemap.xml (https://prohomelab.com/sitemap.xml) - Hugo generates it automatically, and it should build on GitHub Pages exactly the same way it did on the old host.
Step 8. The actual switch: DNS and domain#
Now that the build is verified and search engine verification is confirmed, you can switch the domain itself. It’s best to do this during a quiet period (off-peak hours for your site’s traffic, if you have such a thing) to minimize how noticeable the downtime window is.
On GitHub, in
Settings → Pagesof theprohomelab-pagesrepository, enter the Custom domain:prohomelab.com. GitHub will try to verify DNS right away and will likely show an error - that’s normal, DNS hasn’t switched yet.Right after that, in Cloudflare, in the DNS records for
prohomelab.com, replace the current A records (pointing to the old host) with four A records pointing to GitHub Pages:Type Name Value A @ 185.199.108.153 A @ 185.199.109.153 A @ 185.199.110.153 A @ 185.199.111.153 Leave the mode as DNS only (grey cloud) - don’t enable Cloudflare proxying until GitHub has issued a certificate (next step).
If you use a
wwwsubdomain, addCNAME www → your-username.github.io.You can also add AAAA records - I don’t think I bothered with them, though.
DNS doesn’t update instantly - usually anywhere from a few minutes to an hour, sometimes longer. You can check propagation on whatsmydns.net for the
prohomelab.comdomain, record type A.
Step 9. Wait for the certificate and enable HTTPS#
As soon as GitHub sees that the domain resolves to its IP addresses, it will automatically request a Let’s Encrypt certificate. This usually takes anywhere from a few minutes to a couple of hours. Until the certificate is issued, the site may only be reachable over HTTP or may show a browser warning about an insecure connection - don’t panic, this is an expected temporary state, not a failure.
Once the Enforce HTTPS checkbox appears in Settings → Pages, enable it. From that point on, all http:// traffic will be automatically redirected to https://, which matters both for users and for search engines (Google explicitly factors HTTPS into rankings).
Step 10. Final check before turning off the old host#
Don’t rush to shut down the old hosting. First:
- Open
https://prohomelab.comin a private browser window (to be sure you’re not seeing a cached old version), and if possible, also from a different DNS resolver (e.g., temporarily switch your device’s DNS to1.1.1.1) - to confirm you’re seeing the new version and not the old host via old cache. - The server response headers should include something like
server: GitHub.com- you can check this via the Network tab in your browser’s dev tools. - Go through 10-15 old article URLs (especially ones that rank well or drive traffic) and confirm none of them return a 404.
- Check
sitemap.xmlandrobots.txtagain - now on the live domain. - In Google Search Console and Yandex.Webmaster, manually resubmit the sitemap (even if the address hasn’t changed) - not strictly necessary, but it can slightly speed up the crawlers re-crawling after the IP change.
Only after all of this is confirmed and has stayed stable for at least a couple of days should you turn off (or let lapse) the old hosting.
What to keep monitoring afterward#
For the first one to two weeks after the migration, it’s worth periodically checking:
- Google Search Console → Indexing → Pages - for any sudden spike in 404s or “Page not found” errors;
- Yandex.Webmaster → Indexing → Site diagnostics - the same thing for Yandex;
- your traffic analytics (if you have them set up) - for any sharp drops in organic search traffic.
Small, short-lived ranking fluctuations after an IP/server change are normal - search engines sometimes recalculate technical metrics (response speed, availability). Only worry if, after a couple of weeks, you see a sustained drop or mass 404s - then go back to steps 2 and 7 of this article and recheck them.
Checklist#
- GitHub repository created, public
-
baseURL,permalinks,uglyURLsin Hugo unchanged - Fine-grained token created and added as a secret in Forgejo
-
static/CNAMEwithprohomelab.comadded to the project - Deploy step in
deploy.ymlswitched from FTP to git push into GitHub - Build verified at
your-username.github.io/repo/before switching DNS - Google Search Console / Yandex.Webmaster verification TXT records in place in Cloudflare
-
robots.txtandsitemap.xmlchecked - A records in Cloudflare switched to GitHub Pages, DNS-only mode
- Custom domain added in GitHub Pages settings
- Certificate issued, Enforce HTTPS enabled
- Old article URLs checked for 404s on the live domain
- Sitemap resubmitted in Search Console and Yandex.Webmaster
- Indexing stable after 1-2 weeks, only then is the old host turned off
In the next article of the series, I’ll cover phase two: moving the build itself over to GitHub Actions and turning Forgejo into a backup mirror of the site.
In my case, that was smartape. ↩︎




