This blog is built using Hugo, GitHub, and Cloudflare Pages. It’s a setup I’ve refined to be “zero maintenance”—once it’s configured, all I have to do is write a Markdown file, push it to GitHub, and the website updates itself within seconds.

– initially, I used fucking netlify, though they have a very annoying paywall, I am not sure what happened, but they charged me extra which I blocked it and then they just show me their fucking and annoying “update your credit card”, and basically I could not do anything in dashboard. So I switched to Cloudflare Pages, and it is just perfect, no paywall, no bullshit, just works.

So, with this setup, I add or edits post locally, push it to GitHub, and Cloudflare updates the website automatically. Very simple and efficient.

Let’s get started.


Why this specific tech stack?

  • Hugo: Unlike WordPress which requires databases and PHP servers, Hugo takes your markdown files and generates plain HTML/CSS. This means your website is incredibly fast, extremely secure, and costs nothing to host.
  • GitHub: It’s the standard for version control. Every change you make is tracked, and if something breaks, you can easily roll back.
  • Cloudflare Pages: Cloudflare watches your GitHub repository. Every time you push a new blog post, Cloudflare automatically rebuilds your site and publishes it to the web in seconds across their massive global edge network.

Phase 1: Installing Hugo

Before we can build anything, you need Hugo on your computer.

On Linux (Debian/Ubuntu)

Use the standard package manager:

sudo apt-get install hugo

On MacOS or Windows

The easiest way is using a package manager like Chocolatey or Scoop:

#who cares! just go search on google how to install hugo on mac or windows.

Verify the installation: Open your terminal or command prompt and type:

hugo version

If you see version details, you are ready to go!


Phase 2: Creating Your Website

Navigate to the directory where you want to keep your project. Let’s call our website MyFreshWebsite and initialize it with YAML configuration format:

hugo new site MyFreshWebsite --format yaml
cd MyFreshWebsite

Hugo just scaffolded a directory structure for you. It includes folders like content (where your writing goes), layouts (for custom HTML templates), and static (for images and CSS).

Version Control (Git)

Initialize Git inside this new directory immediately. This allows us to track changes and push to GitHub later.

git init

Phase 3: Adding a Theme

A raw Hugo site looks like a blank white page. We need a theme. For this tutorial (and for my own website), I use the excellent and minimalist PaperMod theme.

The recommended way to install PaperMod is by adding it as a Git Submodule. Inside your Hugo site folder, run:

git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

Now, tell Hugo to use this theme. Open your site config (hugo.yml) and add:

theme: ["PaperMod"]

Phase 4: Basic Configuration

Let’s configure the basics using the standard PaperMod sample configuration. We use YAML format as it is easier to read. Here is a comprehensive hugo.yml structure you can use as a starting point:

baseURL: "https://examplesite.com/"
title: ExampleSite
pagination:
  pagerSize: 5
theme: PaperMod

enableRobotsTXT: true
buildDrafts: false
buildFuture: false
buildExpired: false

minify:
  disableXML: true
  minifyOutput: true

params:
  env: production # to enable google analytics, opengraph, twitter-cards and schema.
  title: ExampleSite
  description: "ExampleSite description"
  keywords: [Blog, Portfolio, PaperMod]
  author: Me
  # author: ["Me", "You"] # multiple authors
  images: ["<link or path of image for opengraph, twitter-cards>"]
  DateFormat: "January 2, 2006"
  defaultTheme: auto # dark, light
  disableThemeToggle: false

  ShowReadingTime: true
  ShowShareButtons: true
  ShowPostNavLinks: true
  ShowBreadCrumbs: true
  ShowCodeCopyButtons: false
  ShowWordCount: true
  ShowRssButtonInSectionTermList: true
  UseHugoToc: true
  disableSpecial1stPost: false
  disableScrollToTop: false
  comments: false
  hidemeta: false
  hideSummary: false
  showtoc: false
  tocopen: false

  assets:
    # disableHLJS: true # to disable highlight.js
    # disableFingerprinting: true
    favicon: "<link / abs url>"
    favicon16x16: "<link / abs url>"
    favicon32x32: "<link / abs url>"
    apple_touch_icon: "<link / abs url>"
    safari_pinned_tab: "<link / abs url>"

  label:
    text: "Home"
    icon: /apple-touch-icon.png
    iconHeight: 35

  # profile-mode
  profileMode:
    enabled: false # needs to be explicitly set
    title: ExampleSite
    subtitle: "This is subtitle"
    imageUrl: "<img location>"
    imageWidth: 120
    imageHeight: 120
    imageTitle: my image
    buttons:
      - name: Posts
        url: posts
      - name: Tags
        url: tags

  # home-info mode
  homeInfoParams:
    Title: "Hi there \U0001F44B"
    Content: Welcome to my blog

  socialIcons:
    - name: x
      url: "https://x.com/"
    - name: stackoverflow
      url: "https://stackoverflow.com"
    - name: github
      url: "https://github.com/"

  analytics:
    google:
      SiteVerificationTag: "XYZabc"
    bing:
      SiteVerificationTag: "XYZabc"
    yandex:
      SiteVerificationTag: "XYZabc"

  cover:
    hidden: true # hide everywhere but not in structured data
    hiddenInList: true # hide on list pages and home
    hiddenInSingle: true # hide on single page

  editPost:
    URL: "https://github.com/<path_to_repo>/content"
    Text: "Suggest Changes" # edit text
    appendFilePath: true # to append file path to Edit link

  # for search
  # https://fusejs.io/api/options.html
  fuseOpts:
    isCaseSensitive: false
    shouldSort: true
    location: 0
    distance: 1000
    threshold: 0.4
    minMatchCharLength: 0
    limit: 10 # refer: https://www.fusejs.io/api/methods.html#search
    keys: ["title", "permalink", "summary", "content"]
menu:
  main:
    - identifier: categories
      name: categories
      url: /categories/
      weight: 10
    - identifier: tags
      name: tags
      url: /tags/
      weight: 20
    - identifier: example
      name: example.org
      url: https://example.org
      weight: 30
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
pygmentsUseClasses: true
markup:
  highlight:
    noClasses: false
    # anchorLineNos: true
    # codeFences: true
    # guessSyntax: true
    # lineNos: true
    # style: monokai

Phase 5: Writing Your First Post

Let’s create something to publish. You can use PaperMod’s comprehensive frontmatter structure by placing a post.md in your archetypes/ directory to use as a template. Then, generate a new post using the Hugo CLI:

hugo new --kind post posts/my-1st-post.md

Open ./content/posts/my-1st-post.md in your text editor. You will see something called “frontmatter” at the top inside --- brackets. Ensure draft is set to false.

---
title: "My 1st post"
date: 2020-09-15T11:30:03+00:00
# weight: 1
# aliases: ["/first"]
tags: ["first"]
author: "Me"
# author: ["Me", "You"] # multiple authors
showToc: true
TocOpen: false
draft: false
hidemeta: false
comments: false
description: "Desc Text."
canonicalURL: "https://canonical.url/to/page"
disableHLJS: true # to disable highlightjs
disableShare: false
disableHLJS: false
hideSummary: false
searchHidden: true
ShowReadingTime: true
ShowBreadCrumbs: true
ShowPostNavLinks: true
ShowWordCount: true
ShowRssButtonInSectionTermList: true
UseHugoToc: true
cover:
    image: "<image path/url>" # image path/url
    alt: "<alt text>" # alt text
    caption: "<text>" # display caption under cover
    relative: false # when using page bundles set this to true
    hidden: true # only hide on current single page
editPost:
    URL: "https://github.com/<path_to_repo>/content"
    Text: "Suggest Changes" # edit text
    appendFilePath: true # to append file path to Edit link
---

This is my very first blog post using Hugo! It wasn’t as hard as I thought.

Previewing Your Site Locally

Let’s see what we built before releasing it to the world.

hugo serve -D

(The -D flag tells Hugo to include draft posts in the preview, just in case).

Open your browser and navigate to http://localhost:1313. You should see your new minimalist blog and your “Hello World” post!

Press Ctrl+C in your terminal to stop the local server.


Phase 6: Pushing to GitHub

Now we have a working site. We need to back it up and get it ready for Cloudflare.

  1. Go to GitHub and log in (or create an account).
  2. Click the + icon in the top right and select New repository.
  3. Name it my-website (or whatever you prefer). Do not initialize it with a README.
  4. Click Create repository.

Ignoring Unnecessary Files

Before pushing your code, it’s a best practice to tell Git to ignore files that are automatically generated (like the compiled site files). In the root directory of your site, create a .gitignore file and add the following lines to it:

public/
.hugo_build.lock

Back in your terminal, inside your MyFreshWebsite folder, run these commands to commit your code and push it to GitHub (replace the URL with your actual repository URL):

git add .
git commit -m "Initial commit of my Hugo blog"
git branch -M main
git remote add origin https://github.com/yourusername/my-website.git
git push -u origin main

Your code is now safe on GitHub!


Phase 7: Deploying to Cloudflare Pages

This is where the magic happens. We will connect Cloudflare to our GitHub repo so that every time we push a new Markdown file, the website updates automatically.

  1. Go to the Cloudflare Dashboard and sign up or log in. It’s free!
  2. Navigate to Workers & Pages -> Create application -> Pages -> Connect to Git.
  3. Choose GitHub.
  4. Authorize Cloudflare to access your GitHub repositories.
  5. Select your my-website repository from the list.

Build Configuration

Cloudflare will detect you are using Hugo. Set these configurations:

  • Build command: hugo --minify
  • Build output directory: public

Environment Variables

To ensure consistency, go to Settings -> Build & deployments -> Environment variables and add:

  • Variable name: HUGO_VERSION
  • Value: 0.146.0 (or whatever version you are using)

Security Headers (_headers)

Instead of a configuration file in the UI, Cloudflare uses a simple text file named _headers inside your static/ directory to manage security. Create static/_headers:

/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff

Once you commit these changes and click Save and Deploy, Cloudflare will trigger its first build. Everything is automated from here!

Cloudflare will take a few seconds to run the build command. Once it says “Success,” click the link provided (it will be something like my-website.pages.dev).

Boom! You are live on the internet!

Next Steps: Custom Domains

If you own a custom domain name (like yourname.com), you can easily add it in the Custom domains tab of your Pages project. Cloudflare provides full DNS proxying, free SSL, and CDN caching out of the box.

Conclusion

You now have a modern, ultra-fast website that costs $0 a month to host on one of the world’s most robust global networks.

To write a new post in the future, the workflow is brilliantly simple:

  1. hugo new --kind post posts/my-new-idea.md
  2. Write your content.
  3. git add . -> git commit -m "new post" -> git push

Cloudflare handles the rest. Welcome to the independent web!