The website has been moved from SSR to SSG.
Infrastructure now runs on Cloudflare Pages, served at the edge for more consistent speeds globally 🌎.
Before the great move
Terms:
- SSR - Server-Side Rendering
- SSG - Static Site Generation
The SSR site was hosted on a DigitalOcean droplet (Basic / 2 vCPUs / 2 GB RAM / 50 GB Disk), along with other services like Keycloak (opens in a new tab), LibreNMS (opens in a new tab) and some self-developed full-stack lifestyle/tracking apps.
The droplet also used to host other public-facing services, including Plausible Analytics (opens in a new tab), which is currently used by this website. As the droplet became increasingly crowded, I moved Plausible back to one of my VMs to make room for Keycloak.
A healthy 4GB swap exists, but the Directus instance started failing to serve on-demand responsive images on the website.
Everything mostly worked, but over time I developed a lingering concern that visitors would run into issues. The most obvious example was responsive images occasionally returning a 404, only to appear after a quick refresh. Visitors outside the APAC region also faced unavoidable latency.
For the bulk of jasonloong.com v2, where SSR was strengthened overall, I looked into offloading the site to SSG since it’s no longer updated as frequently.
AI 🤖 Acceptance
Unless you’re a hermit living in a cave without an internet connection, 2025-2026 wasn't a great period for real software developers, junior or senior (not vibe coders).
Up till the v2 update, most of the website's core skeleton, architectural direction, and schema models were manually thought out and researched, with nights of coding, previewing, and ah-ha 😇 moments.
As of writing this post in 2026, AI is pretty good now, as long as the user knows what to ask for.
From personal experience, a software feature that may have taken me 1 day to implement comfortably, without tests, could be implemented by AI in less than 10 minutes to ~2 hours, depending on whether you are still testing things out or verifying a feature.
With AI being the assistant, I directed it to create a local SSG builder that links up directly to my Directus instance.
Being 🙋🏻♂️ Human
While this website isn't the greatest or most popular in the region, I put genuine effort into writing content directed to readers. Besides giving the rare organic visitor something useful (hopefully entertaining) to read, this blog is also a diary. Somewhere I can look back on the events, projects, and posts that have accumulated over the years.
Here's a quick example from my assistant, TASUKO:
As a blog author publishing content/information on the Internet, I believe I have a responsibility to be transparent about what I publish. That said, TASUKO is also meant to help me soften any crude/straight-to-truth comments that readers may find offensive. 😂
So, going forward, whenever a significant part of a blog post is heavily AI-generated or AI-assisted, I'll declare it accordingly and honestly.
Versioning
While directing AI to build a simpler alternative to Nuxt Content, a TipTap Markdown parse caused some published content to disappear. I only discovered the damage after visiting the website, but Directus’s versioning allowed me to recover it. Why reinvent the wheel, right?
How bad was the damage, and why did versioning matter?
Two tables disappeared from the published Leica M10 Experience post and remained missing for a good two months.
Without the restore, I would have needed to reopen the relevant tabs, go through my old posts and recalculate the numbers, while hoping I could remember what had originally been written.

That saved me 15~30 minutes of frustration; what if you had even more tables? Numbers that may not have been saved anywhere else as backup, or no longer accessible?
Directus Stays
With the help of AI, I smoothly migrated the Directus Docker instance from my droplet to my local VM to use as the main source of truth for all my content.
Directus (opens in a new tab) is pretty mature and stable now, and it's better to let it handle content versioning, which I came to appreciate even more after this mishap.
Directus’s role:
- User Management
- Versioning and source of truth for my blog post content (headless CMS - Content Management System) and photo assets (DAM - Digital Asset Management)
- Master model schema
Why not Nuxt Content
It was mostly a timeline mismatch.
When I made this decision, Nuxt Content v1 and v2 were still file-based and Git-oriented. The SQL-backed storage and collections available in Nuxt Content v3 only arrived in 2025.
The idea of keeping my content as static files governed by my self-hosted GitLab repository was neat, but it did not fit what I needed at the time. I do not deploy and maintain services purely for “fun”, at least not after the initial curiosity wears off. Whether it is a homelab service or this website, it eventually needs to be flexible enough for the people expected to use it.
GUI and user experience
I needed something a non-technical editor could log in to and use without understanding Git, Markdown repositories or deployment pipelines. Heck, even for myself over the years, when I eventually get older and start to forget.
Imagine asking someone from marketing or sales to edit a company website through an online code editor. Technically possible, certainly. Whether they would ever volunteer to do it a second time is another question. 😱
Nuxt Studio (opens in a new tab) eventually provided the kind of visual editing experience I wanted, but that came after this website had already settled on Directus.
Nuxt Content would also have coupled my authoring, custom components and publishing workflow more tightly to the Nuxt ecosystem. The Markdown itself remained portable, but the complete editing and rendering experience would have depended on Nuxt-specific conventions.
I chose Directus because it already provided user management, versioning, structured models, relationships and asset management. More importantly, those features were available when I needed them.
The experience also remains useful beyond this personal website. If I need to build something similar for an SMB, I understand where Directus fits, where it does not, and when its licensing needs to be considered.
✅ SSR - suitable when content must be rendered or personalized at request time
✅ SSG with a custom publisher - suitable when content changes less frequently and can wait for a deliberate build
✅ Custom admin portal - can support either rendering model SSR/SSG and be tailored to the editor’s requirements
Directus was never the issue; my infrastructure requirements simply changed.
Infrastructure
Before moving to SSG, the standard topology looked like this: my DigitalOcean droplet acted as the NGINX reverse proxy, routing requests to public-facing services running either on the droplet itself or through a tunnel to my virtual machines.
Not shown on the diagram:
- Uptime Kuma (opens in a new tab)
- Plausible Analytics (opens in a new tab)
- and many others not relevant to this post 😁
A viable solution before the whole SSG feature was to move the stacks/services back to one of my Virtual Machines, which definitely have more than 2 vCPUs / 2GB RAM.
I could have stopped there, but I didn't want to make my home/homelab a critical public-facing dependency.
Cloudflare Pages & Cloudflare R2
As my AI-assisted research went deeper into the rabbit hole, the performance and infrastructure benefits of SSG, combined with Cloudflare Pages and R2, made the migration worth trying. SSG and JAMstack-style deployments were not new to me, but I deliberately wanted the experience of hosting and maintaining a traditional server-served application first. Having done that for several years, I now had a good reason to give SSG a proper go.
I had nothing to lose: faster speeds, pre-rendered HTML that crawlers can read without depending on my origin server, fewer exposed security vectors, and fewer website downtime notifications whenever my home WAN connection goes down.
The main takeaway is that the site currently uses less than R2’s free 10 GB-month storage allowance, while Internet egress is free.
What changed in the migration:
- Responsive images are no longer generated on demand by Directus. Predetermined WebP and AVIF variants are generated during the build instead.
The important part is the separation: publishing may become unavailable when my homelab is down, but the last deployed website remains online.
oh my WordPress
Between my previous experience, documented in From 2020 and into 2023 # tired of maintenance, and the steady stream of WordPress plugin security advisories, I knew I didn't want my personal website to become another patching schedule.
45.148.10.9 - - [28/Sep/2026:16:14:41 +0800] "POST /blog/?rest_route=/batch%2Fv1 HTTP/1.1" 404 110 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:42 +0800] "POST /blog/?rest_route=/batch/v1 HTTP/1.1" 404 110 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:43 +0800] "POST /blog/wp-json/batch/v1 HTTP/1.1" 404 142 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:44 +0800] "POST /blog/wp-json/Batch/v1 HTTP/1.1" 404 142 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:44 +0800] "POST /blog//wp-json/batch/v1 HTTP/1.1" 404 144 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:44 +0800] "POST /blog/wp-json/batch/v1/ HTTP/1.1" 404 144 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:45 +0800] "POST /blog/blog/wp-json/batch/v1 HTTP/1.1" 404 152 "https://REDACTED" "WordPress/6.4.3"
45.148.10.9 - - [28/Sep/2026:16:14:45 +0800] "POST /blog/wordpress/wp-json/batch/v1 HTTP/1.1" 404 162 "https://REDACTED" "WordPress/6.4.3"
root@REDACTED:/var/log/nginx# cat REDACTED.access.log | grep -i 'wp-admin' | wc -l
457 # Another day of wp-admin attempts, excluding other .env attempts 😂That was one reason I chose Nuxt over WordPress. Moving from SSR to SSG takes the idea further: the public website no longer exposes my CMS or application server with every visitor request. Cloudflare serves generated HTML, JavaScript and images, while Directus and the authoring tools remain elsewhere.
What changed for me
I have three hypervisors running XCP-ng (opens in a new tab), separated by role and priority:
[Storage]- On the most stable platform if possible. Data, backups, memories (photos). This role previously belonged to a Synology DS916+, which has since been retired, with its storage migrated to TrueNAS SCALE.[Router/Firewall]- As of 2026, usable 4G/5G coverage means that even without the main WAN connection, I can still get online to find information or support.[Services/Apps]- Most services and applications, including public-facing ones, live here. Anything requiring greater stability may instead be placed on[Storage].
I had plans to move the public-facing services onto [Storage], but this separation of the website from my homelab became useful sooner than expected.
A TrueNAS VM running on the [Storage] hypervisor recently developed a suspected HBA or cable-path issue, requiring the hypervisor to be shut down for investigation. By then, the website had already been converted to SSG.
The consequences are minor for a personal blog, but the same separation matters much more in a production environment—for example, a company website or critical customer-facing service.

Directus, the admin portal or the publishing workflow may become temporarily unavailable depending on the affected infrastructure, but the last deployed website remains online through Cloudflare Pages and R2.
Nothing is perfect... or free.
Fortunately, the forums and analytics were unaffected during this downtime, as I only needed to shut down the [Storage] hypervisor. Even if I eventually move these public-facing “critical” services onto it, the consequences of an outage remain acceptable: users temporarily lose access to the forums, and I miss some website analytics.
I can repair or upgrade the server without wondering how long the public website has been offline. That, more than a Lighthouse score, is the improvement I wanted.
Moving this website onto new infrastructure was only half the work. The next "feature" was building a publishing pipeline I could trust with the content and photographs accumulated over the years.


