This is the exact stack behind this blog. If you want a super fast, self-deploying tech blog with zero servers, zero monthly bills, and VS Code as your entire CMS — here is how to build it.
By the end you will have:
- A Hugo blog with the PaperMod theme
- VS Code as your editor, file manager, git client, and terminal — all in one
- Automatic deploys to Cloudflare Pages on every
git push
Why this stack
Hugo is a static site generator. It turns markdown files into a complete website in milliseconds. No database, no runtime, nothing to keep running.
PaperMod is a clean Hugo theme with sensible defaults out of the box: reading time, code copy buttons, table of contents, dark mode, tags — all working without configuration.
Cloudflare Pages hosts the output globally with HTTPS, CDN caching, and branch preview URLs — free on the free tier.
GitLab CI is the automation layer. Every push triggers a pipeline that builds the site and deploys it to Cloudflare. You never manually run a deploy.
VS Code handles everything local: editing markdown, previewing files, running the Hugo dev server in the integrated terminal, and committing/pushing via the built-in Source Control panel. You never need a separate CMS.
Prerequisites
- Git installed locally
- VS Code installed
- Node.js + npm installed (for Wrangler)
- A free GitLab account
- A free Cloudflare account
Windows users: all commands in this guide are Linux/macOS shell commands. The easiest path is WSL 2 (Windows Subsystem for Linux) — run
wsl --installin an elevated PowerShell, then open VS Code and install the WSL extension. Everything below works identically inside WSL.
Step 1: Create the site
Open a terminal (VS Code integrated terminal works perfectly — Ctrl+`):
hugo new site my-blog
cd my-blog
git init
git checkout -b main
Open the project in VS Code:
code .
From this point on, VS Code is your CMS. The Explorer panel on the left is your file browser. The integrated terminal runs Hugo. The Source Control panel handles all git operations.
Step 2: Add the PaperMod theme
In the VS Code terminal:
git submodule add --depth 1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
Then open .gitmodules in VS Code and add shallow = true:
[submodule "themes/PaperMod"]
path = themes/PaperMod
url = https://github.com/adityatelange/hugo-PaperMod.git
shallow = true
This keeps CI clones fast by fetching only the latest theme commit.
Step 3: Configure hugo.toml
Open hugo.toml in VS Code (it’s in the root of your project) and replace its contents:
baseURL = "https://your-project.pages.dev/"
languageCode = "en-us"
title = "Your Blog Name"
theme = "PaperMod"
enableRobotsTXT = true
enableGitInfo = true
[pagination]
pagerSize = 10
[permalinks]
posts = "/:slug/"
[taxonomies]
tag = "tags"
category = "categories"
[params]
description = "Your blog description."
author = "Your Name"
ShowReadingTime = true
ShowCodeCopyButtons = true
ShowPostNavLinks = true
ShowBreadCrumbs = true
ShowToc = true
mainSections = ["posts"]
[params.homeInfoParams]
Title = "Your tagline here"
Content = "Short description of what you write about."
[[menu.main]]
identifier = "posts"
name = "Posts"
url = "/posts/"
weight = 10
[outputs]
home = ["HTML", "RSS", "JSON"]
Leave baseURL as a placeholder for now — you will update it with your real URL in Step 6.
Step 4: Write your first post
In the VS Code terminal:
hugo new content posts/my-first-post.md
VS Code will show the new file in the Explorer under content/posts/. Click it to open and edit. The front matter Hugo generates looks like:
---
title: "My First Post"
date: 2026-01-01T00:00:00Z
draft: true
---
Fill it out and set draft: false when the post is ready:
---
title: "My First Post"
date: 2026-01-01T00:00:00Z
draft: false
description: "A short summary of this post."
tags: ["example"]
categories: ["meta"]
---
Your post content here.
Step 5: Push to GitLab
Create a new blank project on GitLab (no README, no .gitignore). Then connect and push from the VS Code terminal:
git remote add origin [email protected]:your-username/my-blog.git
git add .
git commit -m "init: hugo blog with PaperMod"
git push -u origin main
After the first push, you can use VS Code’s Source Control panel (Ctrl+Shift+G) for all future commits and pushes — no terminal needed.
Step 6: Create the Cloudflare Pages project
Install Wrangler:
npm install -g wrangler@4
wrangler login
wrangler whoami
Create the Pages project (one-time only):
wrangler pages project create my-blog --production-branch main
Wrangler prints your site URL: https://my-blog-xxxx.pages.dev. Open hugo.toml in VS Code and update baseURL to that URL. Commit the change:
git add hugo.toml
git commit -m "chore: set baseURL to Cloudflare Pages URL"
Step 7: Create a Cloudflare API token
Your CI pipeline needs permission to deploy.
- Open Cloudflare → API Tokens
- Create Token → Custom token
- Permission: Account → Cloudflare Pages → Edit
- Copy the token — you will not see it again
Also copy your Account ID from the wrangler whoami output.
Step 8: Add the CI pipeline
In VS Code, create a new file .gitlab-ci.yml in the project root and paste:
stages:
- deploy
variables:
GIT_SUBMODULE_STRATEGY: recursive
HUGO_ENV: production
deploy_cloudflare_pages:
stage: deploy
image: node:20
before_script:
- apt-get update -qq && apt-get install -y -qq curl git ca-certificates jq
- HUGO_VERSION=$(curl -fsSL https://api.github.com/repos/gohugoio/hugo/releases/latest | jq -r '.tag_name' | sed 's/v//')
- HUGO_TARBALL="hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
- curl -fsSL -o /tmp/hugo.tar.gz "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_TARBALL}"
- tar -xzf /tmp/hugo.tar.gz -C /tmp
- mv /tmp/hugo /usr/local/bin/hugo && chmod +x /usr/local/bin/hugo
- npm install -g wrangler@4
script:
- hugo --gc --minify
- wrangler pages deploy public --project-name "${CLOUDFLARE_PAGES_PROJECT}" --branch "${CI_COMMIT_REF_NAME}"
rules:
- if: '$CLOUDFLARE_PAGES_PROJECT == null'
when: never
- if: '$CI_COMMIT_BRANCH'
This pipeline automatically fetches the latest Hugo Extended release from the GitHub API on every run — no version pinning to maintain.
Step 9: Add GitLab CI variables
In GitLab → your project → Settings → CI/CD → Variables, add:
| Variable | Value | Notes |
|---|---|---|
CLOUDFLARE_API_TOKEN | your API token | Mark as Masked |
CLOUDFLARE_ACCOUNT_ID | your account ID | |
CLOUDFLARE_PAGES_PROJECT | my-blog | Must match the name from Step 6 |
Step 10: Deploy
Commit and push from VS Code Source Control panel or terminal:
git add .
git commit -m "ci: add GitLab CI pipeline"
git push
Go to GitLab → CI/CD → Pipelines. When the job goes green, your site is live at the Cloudflare Pages URL.
Step 11: Attach a custom domain (optional)
- Cloudflare Pages → your project → Custom domains → Set up a custom domain
- Enter your domain (e.g.
blog.yourdomain.com) - If DNS is managed in the same Cloudflare account: records are created automatically
- If external DNS: add
CNAME blog → your-project.pages.dev - Update
baseURLinhugo.tomlto your real domain, commit, and push
HTTPS is provisioned automatically.
The daily workflow
Once everything is set up, writing a post is:
- New post:
hugo new content posts/post-title.mdin the VS Code terminal - Edit: Click the file in the Explorer panel, write in VS Code
- Publish: In Source Control panel — stage → commit → push
That’s it. GitLab CI handles the build and deploy. You never touch a server.