<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jade Angco — Blog</title>
    <link>https://jadeangco-portfolio.onrender.com/blog</link>
    <description>Practical notes on SvelteKit, Vue.js, Nginx, Cloudflare Tunnel, and freelance web development.</description>
    <language>en</language>
    <atom:link href="https://jadeangco-portfolio.onrender.com/blog/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>The 27-part server playbook behind my client hosting (overview)</title>
      <link>https://jadeangco-portfolio.onrender.com/blog/ubuntu-server-cloudflare-tunnel-setup-guide</link>
      <guid isPermaLink="true">https://jadeangco-portfolio.onrender.com/blog/ubuntu-server-cloudflare-tunnel-setup-guide</guid>
      <pubDate>Tue, 29 Sep 2026 00:00:00 GMT</pubDate>
      <description>Static IP to snapshots, tunnels to WAF — everything a single Ubuntu server needs to host client sites safely. The full implementation is a service I deliver, not a tutorial. Here's the map.</description>
      <category>Ubuntu</category>
      <category>Nginx</category>
      <category>Cloudflare Tunnel</category>
      <category>DevOps</category>
      <category>Hosting</category>
      <content:encoded><![CDATA[<p>Anyone can rent a VPS. Turning one into hosting you&#39;d trust with <em>paying clients&#39;</em> websites is a different job — networking, tunnels, certificates, process managers, monitoring, backups, and hardening, all wired together so the box is safe to leave running unattended.</p>
<p>I built exactly that as a 27-section playbook, and it&#39;s what runs all five of my <a href="/#live-sites">live client sites</a>. This post is the map of what&#39;s inside. The step-by-step implementation — commands, configs, and scripts — is what I <strong>deliver for clients</strong>, because a half-followed server guide is how people lock themselves out of SSH on a Friday night.</p>
<h2>What&#39;s inside</h2>
<p><strong>Foundation &amp; networking</strong> — static LAN addressing done safely (with rollback), plus optional KVM virtual machines on a bridged network so a client&#39;s app can live in its own isolated VM.</p>
<p><strong>The core tunnel stack</strong> — Nginx installed and verified, <code>cloudflared</code> installed and authenticated, your domain (say, <code>example.com</code>) connected to Cloudflare, one shared tunnel created, and a wildcard origin certificate so traffic is encrypted all the way to the server — Full Strict, not Almost.</p>
<p><strong>Serving sites &amp; apps</strong> — static sites as folders, Node/SvelteKit apps behind reverse proxies on their own ports, PM2 keeping every app alive across crashes <em>and</em> reboots (including the <code>pm2 save</code> step everyone forgets), and the full tunnel ingress config tying every hostname together with a safe catch-all.</p>
<p><strong>Staying alive</strong> — a health-check script that curls your real public URL every 5 minutes and restarts the tunnel when the connection silently drops (systemd alone can&#39;t see that), plus Cloudflare-side tunnel alerts for when the whole box is unreachable.</p>
<p><strong>Private admin access</strong> — a WireGuard-based VPN so <em>you</em> reach SSH, logs, and dashboards from anywhere with zero open inbound ports, friendly hostnames instead of IPs, and remote VM management without X11 pain.</p>
<p><strong>Backups without downtime</strong> — live disk snapshots of running VMs on a nightly schedule with retention pruning, triggerable on demand from your phone before risky changes.</p>
<p><strong>Hardening &amp; hygiene</strong> — edge WAF with managed rules, bot protection, per-site rate limits (scoped by hostname so clients never affect each other), automatic security patching, log rotation, and resource monitoring bound to the private network only.</p>
<p><strong>Operations</strong> — a one-command script that adds a new static site (folder, Nginx block, tunnel route, DNS, reloads) in about five minutes, a checklist for the manual SSR path, a 20-row troubleshooting table built from real failures, and a per-site-type quick reference.</p>
<h2>Why this isn&#39;t a copy-paste tutorial</h2>
<p>Three honest reasons:</p>
<ol>
<li><strong>The dangerous steps are order-dependent.</strong> Bridge networking, static IPs, and firewall changes can lock you out of your own server if sequenced wrong. My playbook is sequenced from scars, not theory.</li>
<li><strong>Every server is different.</strong> Cloud VM vs home lab, single site vs multi-client, static vs SSR — the right choices change, and a generic guide can&#39;t make them for you.</li>
<li><strong>The value is the guarantee.</strong> When I deploy this, I verify every layer live, hand over plain-English notes, and remain the person to call. A tutorial can&#39;t do that.</li>
</ol>
<h2>What &quot;done for you&quot; looks like</h2>
<ul>
<li><strong>Audit</strong> — I look at what you&#39;re running now and tell you straight whether this architecture fits (sometimes your current hosting is fine — I&#39;ll say so)</li>
<li><strong>Build</strong> — server hardened, tunnel live, your sites migrated with zero-downtime DNS cutover</li>
<li><strong>Handover</strong> — monitoring verified, snapshots scheduled, the 5-minute new-site routine in your hands</li>
<li><strong>Backup human</strong> — when something breaks at 11pm, you message me, not a support forum</li>
</ul>
<p>Five sites on one VPS instead of five hosting bills typically pays for the setup within months — and every site gets the same live uptime badge my clients have. <a href="/#contact">Contact me</a> with your current setup and I&#39;ll scope it honestly.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How I host 5 websites on one server with Nginx + Cloudflare Tunnel</title>
      <link>https://jadeangco-portfolio.onrender.com/blog/host-multiple-sites-nginx-cloudflare-tunnel</link>
      <guid isPermaLink="true">https://jadeangco-portfolio.onrender.com/blog/host-multiple-sites-nginx-cloudflare-tunnel</guid>
      <pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate>
      <description>One VPS, five live client sites, zero open ports — the architecture I run for every client site, and how to get the full setup done for yours.</description>
      <category>Nginx</category>
      <category>Cloudflare Tunnel</category>
      <category>DevOps</category>
      <category>Hosting</category>
      <content:encoded><![CDATA[<p>I host five live websites — a hotel, a commercial building, a restobar, a corporation, and their parent company site — on a single server. One VPS bill instead of five. No open ports 80 or 443. Every site shows a live badge on <a href="/#live-sites">my portfolio</a> proving it&#39;s up right now.</p>
<p>Here&#39;s the architecture, honestly explained — and at the end, how to get the whole thing built for you.</p>
<h2>The shape of it</h2>
<pre><code class="language-text">Visitor → Cloudflare edge (DNS + SSL) → Cloudflare Tunnel → Nginx → your sites
</code></pre>
<p>Three jobs, three tools:</p>
<ul>
<li><strong>Cloudflare</strong> handles DNS and the public SSL certificate. Free plan is enough.</li>
<li><strong>Cloudflare Tunnel</strong> (<code>cloudflared</code>) is an <em>outbound-only</em> connection from the server. The firewall stays shut — there is nothing to attack.</li>
<li><strong>Nginx</strong> routes each domain to the right folder or app on the machine, e.g. <code>shop.example.com</code> → one folder, <code>app.example.com</code> → a Node app on port 3000.</li>
</ul>
<p>A basic static site block looks like this — this part is standard Nginx anyone can look up:</p>
<pre><code class="language-nginx">server {
    listen 80;
    server_name shop.example.com;

    root /var/www/shop;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
</code></pre>
<p>Simple. The parts that actually take experience are everything around it.</p>
<h2>What &quot;everything around it&quot; means</h2>
<p>Getting from that snippet to five always-on client sites took a full playbook:</p>
<ul>
<li><strong>One shared tunnel</strong> carrying every hostname, with a catch-all rule so unknown domains get a clean 404 instead of leaking somewhere</li>
<li><strong>Wildcard origin SSL</strong> so browser-to-server traffic is encrypted end to end (Full Strict), not just browser-to-Cloudflare</li>
<li><strong>Node apps kept alive</strong> across crashes and reboots (PM2 + startup hooks that people always forget to save)</li>
<li><strong>Health checks that restart the tunnel</strong> when the connection silently drops — <code>systemctl</code> alone won&#39;t catch that</li>
<li><strong>WAF + rate limiting</strong> at the edge, scoped per client so one site&#39;s rules never affect another&#39;s</li>
<li><strong>Auto security patching, log rotation, and resource monitoring</strong> so the box is safe to leave running unattended</li>
<li><strong>Private admin access</strong> (VPN, no open SSH) and <strong>automated snapshots</strong> before risky changes</li>
<li><strong>A one-command script</strong> so adding site number six takes five minutes, not an afternoon</li>
</ul>
<p>That&#39;s the difference between &quot;a server block&quot; and &quot;hosting you can sell to clients.&quot; The snippet above is 5% of it.</p>
<h2>Proof, not promises</h2>
<p>I don&#39;t just write about this stack — I run it. All five client sites on <a href="/#live-sites">my Live Sites list</a> run on exactly this architecture, each with a badge that pings the real URL from your browser and shows the round-trip time. If a tunnel ever drops, the badge says Offline. No fake &quot;99.9%&quot;.</p>
<h2>Get the full setup — done for you</h2>
<p>The complete implementation playbook (tunnel config, SSL, monitoring scripts, hardening, automation) is something I deploy <strong>for clients</strong>, not something I publish. Here&#39;s what you get:</p>
<ul>
<li><strong>Your sites migrated</strong> onto one efficient server (or your existing one, hardened)</li>
<li><strong>DNS, SSL, tunnel, monitoring, and backups</strong> configured and verified live</li>
<li><strong>A 5-minute routine</strong> for adding future sites, handed over with plain-English notes</li>
<li><strong>Someone to call</strong> when something breaks at 11pm — me</li>
</ul>
<p>One server instead of five hosting bills usually pays for the setup within months. <a href="/#contact">Contact me</a> with what you&#39;re running now, and I&#39;ll tell you honestly whether this architecture fits — including when the answer is &quot;your current hosting is fine.&quot;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Deploying a SvelteKit static site on Render</title>
      <link>https://jadeangco-portfolio.onrender.com/blog/deploy-sveltekit-render-static</link>
      <guid isPermaLink="true">https://jadeangco-portfolio.onrender.com/blog/deploy-sveltekit-render-static</guid>
      <pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate>
      <description>adapter-static, a render.yaml blueprint, and the two gotchas that bite everyone — fallback pages and prerendered routes.</description>
      <category>SvelteKit</category>
      <category>Render</category>
      <category>Deployment</category>
      <content:encoded><![CDATA[<p>My portfolio is a SvelteKit app served as plain static files on Render. No Node server, no cold starts, free hosting. Here&#39;s the full setup — this site is the proof it works.</p>
<h2>1. adapter-static</h2>
<p>Install it and point the output at a <code>build</code> folder:</p>
<pre><code class="language-js">// svelte.config.js
import adapter from &#39;@sveltejs/adapter-static&#39;;

const config = {
  kit: {
    adapter: adapter({
      pages: &#39;build&#39;,
      assets: &#39;build&#39;,
      fallback: &#39;404.html&#39;,
      precompress: true,
      strict: true
    })
  }
};
</code></pre>
<p>Three settings matter:</p>
<ul>
<li><strong><code>fallback: &#39;404.html&#39;</code></strong> — generates a fallback page so unknown URLs (and client-rendered routes like my project modals&#39; deep links) don&#39;t hit a dead end. It also doubles as your custom 404 page.</li>
<li><strong><code>precompress: true</code></strong> — emits gzip + brotli versions of everything at build time. Free bandwidth savings.</li>
<li><strong><code>strict: true</code></strong> — fails the build if any route isn&#39;t prerenderable, so you catch dynamic-route mistakes before deploying, not after.</li>
</ul>
<h2>2. Prerender every route, including dynamic ones</h2>
<p>Static output means every URL must exist at build time. Normal pages just work. Dynamic routes like my <code>/blog/[slug]</code> pages need an <code>entries()</code> function telling SvelteKit which slugs to generate:</p>
<pre><code class="language-ts">// src/routes/blog/[slug]/+page.ts
export const prerender = true;

export function entries() {
  return posts.map((p) =&gt; ({ slug: p.slug }));
}
</code></pre>
<p>Forget this and the build fails under <code>strict</code> — which is exactly what you want.</p>
<h2>3. The Render blueprint</h2>
<p>One <code>render.yaml</code> in the repo root turns deploys into &quot;push to branch, done&quot;:</p>
<pre><code class="language-yaml">services:
  - type: web
    name: jadeangco-portfolio
    env: static
    buildCommand: npm ci &amp;&amp; npm run build
    staticPublishPath: ./build
</code></pre>
<p>Connect it via <strong>New → Blueprint</strong> in the Render dashboard. Every push rebuilds and republishes. Keep the publish path and the adapter output (<code>build</code>) in sync or you&#39;ll deploy an empty site — I speak from experience.</p>
<h2>4. Don&#39;t ship megabytes</h2>
<p>Before my last deploy, four PNG screenshots weighed <strong>7.8 MB</strong>. I converted every referenced image to WebP (same folder, code updated) and the total dropped to <strong>548 KB</strong> — a 93% cut with no visible difference. Image weight is the cheapest Core Web Vitals win on a portfolio.</p>
<p>The short version: static adapter + strict prerendering + blueprint deploys + light images. If you want this exact setup, it&#39;s all running <a href="/">right here</a> — and if you&#39;d rather hire someone to do it for your site, <a href="/#contact">that&#39;s me</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>SvelteKit vs Nuxt after building real sites in both</title>
      <link>https://jadeangco-portfolio.onrender.com/blog/sveltekit-vs-nuxt</link>
      <guid isPermaLink="true">https://jadeangco-portfolio.onrender.com/blog/sveltekit-vs-nuxt</guid>
      <pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate>
      <description>I built tourism sites in Nuxt and apps in SvelteKit. Here's my honest comparison — and which one I'd pick for your project.</description>
      <category>SvelteKit</category>
      <category>Nuxt</category>
      <category>Vue.js</category>
      <category>Comparison</category>
      <content:encoded><![CDATA[<p>Most framework comparisons are written by people who read the docs. I built production sites in both: tourism websites in <strong>Nuxt</strong> (Elnido Hideaway, TravelHive) and live apps in <strong>Svelte/SvelteKit</strong> (Parinig, Reziofy). Here&#39;s what actually differs day to day.</p>
<h2>Reactivity: runes vs refs</h2>
<p>Svelte 5 runes (<code>$state</code>, <code>$derived</code>) feel like writing plain JavaScript that happens to be reactive. No <code>.value</code> unwrapping, no thinking about refs vs reactive objects:</p>
<pre><code class="language-svelte">let activeFilter = $state(&quot;All&quot;);
const filtered = $derived(
  activeFilter === &quot;All&quot; ? projects : projects.filter((p) =&gt; p.status === activeFilter)
);
</code></pre>
<p>Vue&#39;s Composition API is excellent, but <code>.value</code> ceremony adds up across a large codebase. For speed of writing, Svelte wins.</p>
<h2>Ecosystem: Nuxt wins on breadth</h2>
<p>Need auth, i18n, image optimization, content? Nuxt has a first-party module for all of it. SvelteKit&#39;s ecosystem is younger — you&#39;ll assemble more yourself. For a content-heavy marketing site with a team, Nuxt&#39;s module catalog is a genuine advantage.</p>
<h2>Output and performance</h2>
<p>Both ship fast static sites. Svelte&#39;s compiler approach (no virtual DOM, tiny runtime) gives it the edge in bundle size on interactive apps — noticeable on Philippine mobile data, which is the network half my users are on.</p>
<h2>When I&#39;d pick which</h2>
<ul>
<li><strong>Pick Nuxt</strong> for content sites with a team, when you need its module ecosystem, or when hiring Vue developers later matters.</li>
<li><strong>Pick SvelteKit</strong> for interactive apps, dashboards, and solo/small-team builds where velocity matters most.</li>
</ul>
<p>My portfolio runs SvelteKit; my tourism sites run Nuxt. Both choices were right for those projects. If you&#39;re deciding for yours, <a href="/#contact">describe it to me</a> and I&#39;ll give you a straight answer — including when the answer is &quot;plain HTML is enough.&quot;</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
