<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ngoc Thach's Space</title><link>https://pngocthach.github.io/</link><description>Recent content on Ngoc Thach's Space</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 08 Mar 2026 19:25:00 +0700</lastBuildDate><atom:link href="https://pngocthach.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>How I Set Up My Personal Blog — Fast, Beautiful, and Free</title><link>https://pngocthach.github.io/p/how-i-set-up-my-personal-blog-fast-beautiful-and-free/</link><pubDate>Sun, 08 Mar 2026 19:25:00 +0700</pubDate><guid>https://pngocthach.github.io/p/how-i-set-up-my-personal-blog-fast-beautiful-and-free/</guid><description>&lt;p&gt;If you want a personal blog that is &lt;strong&gt;fast, beautiful, and completely free&lt;/strong&gt; — no ads, quick loading, and a full-featured comment system (emoji, reactions, anonymous commenting&amp;hellip;) — this combo is the perfect choice in 2026:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hugo&lt;/strong&gt; (a blazing-fast static site generator)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stack Theme&lt;/strong&gt; (modern, minimalist design with a gorgeous dark mode)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Pages&lt;/strong&gt; (free hosting)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Waline&lt;/strong&gt; (open-source comment system, self-hosted on Vercel + Neon Postgres, supports anonymous comments that appear instantly)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;A GitHub account (public repo)&lt;/li&gt;
&lt;li&gt;A Vercel account (free, sign in with GitHub)&lt;/li&gt;
&lt;li&gt;A Neon account (free, serverless Postgres)&lt;/li&gt;
&lt;li&gt;A machine with Git + Hugo installed (extended version recommended)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-clone-the-hugo-stack-template-and-set-up-the-basic-site"&gt;Step 1: Clone the Hugo Stack Template and Set Up the Basic Site
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to the official starter repo:&lt;br&gt;
&lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack-starter" target="_blank" rel="noopener"
 &gt;https://github.com/CaiJimmy/hugo-theme-stack-starter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Use this template&lt;/strong&gt; → &lt;strong&gt;Create a new repository&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name your repo:
&lt;ul&gt;
&lt;li&gt;For a root domain: &lt;code&gt;username.github.io&lt;/code&gt; (e.g., pngocthach.github.io)&lt;/li&gt;
&lt;li&gt;For a subpath: any name (e.g., my-blog) → your domain will be &lt;code&gt;username.github.io/my-blog&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone the repo to your machine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/username/username.github.io.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; username.github.io
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Hugo if you haven&amp;rsquo;t already:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS: &lt;code&gt;brew install hugo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows: use Scoop/Chocolatey or download the extended binary from &lt;a class="link" href="https://github.com/gohugoio/hugo/releases" target="_blank" rel="noopener"
 &gt;https://github.com/gohugoio/hugo/releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Linux (varies by distro): &lt;code&gt;sudo apt install hugo&lt;/code&gt; (or extended)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Verify: &lt;code&gt;hugo version&lt;/code&gt; (should be ≥ 0.120)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the site locally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Open &lt;a class="link" href="http://localhost:1313" target="_blank" rel="noopener"
 &gt;http://localhost:1313&lt;/a&gt; — if you see the Stack demo page, you&amp;rsquo;re good to go.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deploy to GitHub Pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to your repo&amp;rsquo;s Settings → Pages&lt;/li&gt;
&lt;li&gt;Source: Deploy from a branch → Branch: main → Folder: / (root) → Save&lt;/li&gt;
&lt;li&gt;The template already includes a GitHub Actions workflow (&lt;code&gt;.github/workflows/hugo.yml&lt;/code&gt;), so every push will automatically trigger a build.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your site will be live within 1–3 minutes at &lt;a class="link" href="https://username.github.io" target="_blank" rel="noopener"
 &gt;https://username.github.io&lt;/a&gt; (or &lt;code&gt;/my-blog&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-2-set-up-the-waline-comment-backend-vercel--neon-postgres"&gt;Step 2: Set Up the Waline Comment Backend (Vercel + Neon Postgres)
&lt;/h2&gt;&lt;p&gt;Waline is a lightweight, open-source comment system that supports anonymous commenting and beautiful emoji/reactions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Deploy Waline on Vercel (free):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visit: &lt;a class="link" href="https://waline.js.org/en/guide/deploy/vercel.html" target="_blank" rel="noopener"
 &gt;https://waline.js.org/en/guide/deploy/vercel.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Deploy&lt;/strong&gt; button (blue)&lt;/li&gt;
&lt;li&gt;Sign in to Vercel with GitHub → Name your project (e.g., &lt;code&gt;my-waline-backend&lt;/code&gt;) → Create&lt;/li&gt;
&lt;li&gt;Wait for deployment (1–2 minutes) → Copy your backend URL: &lt;a class="link" href="https://my-waline-backend.vercel.app" target="_blank" rel="noopener"
 &gt;https://my-waline-backend.vercel.app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Neon Postgres database (free tier):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Vercel Dashboard → &lt;strong&gt;Storage&lt;/strong&gt; tab → &lt;strong&gt;Create Database&lt;/strong&gt; → Select &lt;strong&gt;Neon&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Region: &lt;strong&gt;Singapore (aws-ap-southeast-1)&lt;/strong&gt; (lowest latency from Southeast Asia)&lt;/li&gt;
&lt;li&gt;Create the DB → Open in Neon console&lt;/li&gt;
&lt;li&gt;In the Neon SQL Editor → Paste the init script from:&lt;br&gt;
&lt;a class="link" href="https://github.com/walinejs/waline/blob/main/assets/waline.pgsql" target="_blank" rel="noopener"
 &gt;https://github.com/walinejs/waline/blob/main/assets/waline.pgsql&lt;/a&gt;&lt;br&gt;
→ Run to create the tables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Connect Neon to Vercel:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Vercel → Storage → Select the Neon DB you just created → &lt;strong&gt;Connect&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;all Environments&lt;/strong&gt;: Development, Preview, Production&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Preview&lt;/strong&gt; for branching (uncheck Production if not needed)&lt;/li&gt;
&lt;li&gt;Custom Prefix: Leave &lt;strong&gt;blank&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Connect → Vercel will automatically inject the environment variables&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the admin account:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visit: &lt;a class="link" href="https://my-waline-backend.vercel.app/ui/register" target="_blank" rel="noopener"
 &gt;https://my-waline-backend.vercel.app/ui/register&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Register the first user → this account becomes the admin&lt;/li&gt;
&lt;li&gt;Log in at &lt;code&gt;/ui&lt;/code&gt; to manage comments later (e.g., review spam)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-3-integrate-waline-into-hugo-stack"&gt;Step 3: Integrate Waline into Hugo Stack
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Edit the config file (&lt;code&gt;config/_default/params.toml&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;waline&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waline&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;serverURL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://waline-backend-iota.vercel.app&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# Replace with your Vercel URL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;reaction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://unpkg.com/@waline/emojis@1.0.1/tw-emoji&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://unpkg.com/@waline/emojis@1.0.1/weibo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Override the template to display full URLs (easier to manage in the admin panel)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;During deployment, you may notice that Waline stores comments using only the relative path (e.g., &lt;code&gt;/p/post-name&lt;/code&gt;) instead of the full URL (&lt;code&gt;https://yourdomain.com/p/post-name&lt;/code&gt;), making it very hard to identify which post a comment belongs to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To fix this, create the file: &lt;code&gt;layouts/partials/comments/provider/waline.html&lt;/code&gt; to override the theme&amp;rsquo;s default.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;File content:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;//unpkg.com/@waline/client@v2/dist/waline.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;//unpkg.com/@waline/client@v2/dist/waline.css&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;stylesheet&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;waline&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;waline-container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;waline-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;l1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;--waline-font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;waline-container&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;wl-count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kc"&gt;text&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kc"&gt;color&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $permalink := .Permalink -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- with .Site.Params.comments.waline -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $config := dict &amp;#34;el&amp;#34; &amp;#34;#waline&amp;#34; &amp;#34;dark&amp;#34; `html[data-scheme=&amp;#34;dark&amp;#34;]` &amp;#34;path&amp;#34; $permalink -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $replaceKeys := dict &amp;#34;serverurl&amp;#34; &amp;#34;serverURL&amp;#34; &amp;#34;requiredmeta&amp;#34; &amp;#34;requiredMeta&amp;#34; &amp;#34;wordlimit&amp;#34; &amp;#34;wordLimit&amp;#34; &amp;#34;pagesize&amp;#34; &amp;#34;pageSize&amp;#34; &amp;#34;imageuploader&amp;#34; &amp;#34;imageUploader&amp;#34; &amp;#34;texrenderer&amp;#34; &amp;#34;texRenderer&amp;#34; &amp;#34;turnstilekey&amp;#34; &amp;#34;turnstileKey&amp;#34; -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- range $key, $val := . -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{- if ne $val nil -}} 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{- $replaceKey := index $replaceKeys $key -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{- $k := default $key $replaceKey -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{- $config = merge $config (dict $k $val) -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{- end -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- end -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;waline-config&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;jsonify&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;safeJS&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;/// Waline client configuration see: https://waline.js.org/en/reference/client.html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;walineConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;waline-config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Waline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walineConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- end -}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;.Permalink&lt;/code&gt; will automatically include the full URL (with domain), so the Waline admin panel displays comments as &lt;code&gt;https://yourblog.com/p/post-name/&lt;/code&gt; instead of a relative path — making them much easier to identify.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-4-writing-new-posts-and-updating-your-blog"&gt;Step 4: Writing New Posts and Updating Your Blog
&lt;/h2&gt;&lt;p&gt;Once the scaffolding is set up, writing new posts is incredibly simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a new post&lt;/strong&gt;: Open a terminal and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new content post/your-post-title/index.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a new folder inside &lt;code&gt;content/post/&lt;/code&gt; along with an &lt;code&gt;index.md&lt;/code&gt; file — this is where you&amp;rsquo;ll write your post content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write your content&lt;/strong&gt;: Open the newly created &lt;code&gt;index.md&lt;/code&gt;. You&amp;rsquo;ll see a section at the top (between &lt;code&gt;+++&lt;/code&gt;) called the &lt;strong&gt;Front Matter&lt;/strong&gt; — this is where metadata like title, date, description, and cover image goes. Everything below is your post content, written in standard Markdown.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Preview locally&lt;/strong&gt;: Run &lt;code&gt;hugo server&lt;/code&gt; to see how your post looks before publishing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Publish (This is all it takes)&lt;/strong&gt;: When you&amp;rsquo;re happy with your post, run these 3 basic Git commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;Add new post: Your Post Title&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As soon as you push, GitHub Actions will handle everything else. Within about a minute, your new post will appear live on your blog!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Total cost: &lt;strong&gt;$0&lt;/strong&gt; (the free tiers of GitHub Pages, Vercel, and Neon are more than sufficient for a personal blog).&lt;/li&gt;
&lt;li&gt;Advantages: A full-featured comment system with anonymous support, instant display, emoji, and reactions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automation&lt;/strong&gt;: Once the setup is done, your only job is to write in Markdown and push to GitHub. GitHub Actions takes care of building and deploying — your post goes live automatically.&lt;/li&gt;
&lt;li&gt;Drawback: If you experience a sudden traffic spike, you may need to upgrade your Neon/Vercel plan — but this is extremely rare for a personal blog.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In just a few simple steps and at zero cost, you have a sleek, professional blog to write on freely. Now it&amp;rsquo;s just a matter of making time to publish! Thanks for reading, and see you in the next post. If you run into any issues or have questions, feel free to leave a comment below!&lt;/p&gt;
&lt;h2 id="references"&gt;References
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hugo Theme Stack Starter Template&lt;/strong&gt; (official repo to clone and get started quickly):&lt;br&gt;
&lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack-starter" target="_blank" rel="noopener"
 &gt;https://github.com/CaiJimmy/hugo-theme-stack-starter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hugo Theme Stack Official Documentation&lt;/strong&gt; (comments config, Waline, and general customization):&lt;br&gt;
&lt;a class="link" href="https://stack.jimmycai.com/" target="_blank" rel="noopener"
 &gt;https://stack.jimmycai.com/&lt;/a&gt;&lt;br&gt;
Comments section: &lt;a class="link" href="https://stack.jimmycai.com/config/comments" target="_blank" rel="noopener"
 &gt;https://stack.jimmycai.com/config/comments&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hugo Official Guide: Host and Deploy on GitHub Pages&lt;/strong&gt; (deployment with GitHub Actions):&lt;br&gt;
&lt;a class="link" href="https://gohugo.io/host-and-deploy/host-on-github-pages/" target="_blank" rel="noopener"
 &gt;https://gohugo.io/host-and-deploy/host-on-github-pages/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Waline Official Documentation&lt;/strong&gt; (Vercel deployment, client config, ecosystem):&lt;br&gt;
&lt;a class="link" href="https://waline.js.org/en/" target="_blank" rel="noopener"
 &gt;https://waline.js.org/en/&lt;/a&gt;&lt;br&gt;
Deploy on Vercel: &lt;a class="link" href="https://waline.js.org/en/guide/deploy/vercel.html" target="_blank" rel="noopener"
 &gt;https://waline.js.org/en/guide/deploy/vercel.html&lt;/a&gt;&lt;br&gt;
Client config (path, lang, reaction): &lt;a class="link" href="https://waline.js.org/en/reference/client/props.html" target="_blank" rel="noopener"
 &gt;https://waline.js.org/en/reference/client/props.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neon Docs: Integrate with Vercel&lt;/strong&gt; (connecting Neon Postgres with Vercel, env vars, branching):&lt;br&gt;
&lt;a class="link" href="https://neon.com/docs/guides/vercel-overview" target="_blank" rel="noopener"
 &gt;https://neon.com/docs/guides/vercel-overview&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Source code of this blog&lt;/strong&gt; (for a real-world reference):&lt;br&gt;
&lt;a class="link" href="https://github.com/pngocthach/pngocthach.github.io" target="_blank" rel="noopener"
 &gt;https://github.com/pngocthach/pngocthach.github.io&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>