Building a Personal Site with Oink, from Setup to Publication
The official Oink tutorial is comprehensive, but connecting the folders, configuration, and commands can still be confusing the first time, especially without much frontend experience. These notes from building YHY Study Website follow one path: understand Oink, learn how content/ is organized, and then write and publish a blog post.

What Hugo and Oink each do
Hugo is a static site generator: it reads Markdown and configuration and produces HTML. Oink is a Hugo theme: it controls the appearance of sidebars, cards, callouts, code blocks, and other components.
Content and appearance are maintained separately:
- Site repository (
my-project-docs): Markdown articles andhugo.ymlconfiguration - Theme repository, github.com/pgsty/oink: templates and styles
The site declares the Oink theme in hugo.yml, and go.mod pins its version, currently v0.6.0. When you run hugo server, Hugo:
- Reads articles and configuration from the site repository.
- Reads templates and styles from the theme module downloaded according to
go.mod. - Renders the content with the theme to generate web pages.
When writing documentation rather than changing the theme, hugo server is enough to remember. You do not need to start with make dev.
Local preview: get the site running first
Enter the project directory and start the server:
Open http://localhost:1313/ in a browser. Saving a changed .md file under content/ automatically refreshes the page.
For a more complete rerender after each change, useful when investigating styling problems:
-DFE: includes draft and future-dated pages for writing.--disableFastRender: disables fast rendering to avoid discrepancies caused by incremental updates.
Organizing content/
Oink does not maintain a separate navigation database: the folder structure on disk determines both the sidebar structure and URL paths.
Three common concepts:
| Concept | Meaning |
|---|---|
| Section | A folder with _index.md; this level also has its own entry page |
| Page | A .md file in a folder, or index.md in a subfolder |
| Sibling | Pages in the same directory, ordered with weight: 10/20/30… |
The root content structure of this site:
Two ways to store an article:
| Format | Example path | Suitable for |
|---|---|---|
| Single file | content/blog/my-post.md | Text posts with images stored in static/ |
| Page bundle | content/blog/my-post/index.md plus images in the same directory | Posts with multiple images kept beside the text |
The first hugo.yml keys to learn
You do not need to read the whole configuration at once. Start with these fields:
| Key | Purpose |
|---|---|
title | Site name shown in browser tabs, the navbar, and elsewhere |
params.productionURL + baseURL | Full production address; affects sitemaps, RSS, and absolute links |
params.github_repo | Content repository used by buttons such as “Edit this page” |
params.copyright | Footer copyright information |
languages.zh.menus.main | Navbar menus: Experience, Learning, Blog, and Links |
baseURL is often tied to productionURL with a YAML anchor:
&productionURL defines the name; *productionURL references it. Changing one value updates the whole site.
Writing a blog post in five steps
- Choose a filename — Create a
.mdfile undercontent/blog/. Its filename becomes part of the URL, so use an English slug, such asoink-site-setup-notes.md→/blog/oink-site-setup-notes/. - Add front matter — Metadata at the top should include at least
title,date, anddescription.linkTitleis the shorter title displayed in lists and cards. - Write the body — Adjust syntax copied from Obsidian, as explained below. Add callouts, step lists, heading anchors, and images as needed.
- Preview locally — Run
hugo serverand open/blog/to check the card and article page. - Publish —
git add→git commit→git push; GitHub Actions builds and updates GitHub Pages.
A front matter template:
Moving from Obsidian to Oink: syntax comparison
| Obsidian | Oink / Markdown | What to do |
|---|---|---|
==highlight== | **bold** | Replace throughout |
[[wikilink]] | [text](/path/) or an external link | Use an actual link |
Image ![[x.png]] |  | See below |
Common Oink components are described in the component documentation:
Callouts
Step lists — Start each item with 1. and add {.steps} at the end, as in the five steps above.
Heading anchors — Use ## Section {#id}. The {#id} is not displayed and supports in-page links such as [text](#id).
Images and captions — Put {caption="..."} on the next line after the image, not on the same line as :
- Global images go in
static/images/...and are referenced as/images/.... - Page-bundle images sit beside
index.mdand use relative paths.
Four make commands for theme development
The four commands in Makefile are aliases. Windows often has no make, and make dev / make check require the theme source at ../oink.
| Command | Theme source | Suitable for |
|---|---|---|
make dev | Local ../oink | Quick previews while changing the theme |
make check | Local ../oink | Running npm test after theme changes |
make build | Version pinned in go.mod | Production builds matching the deployed site |
make serve | Version pinned in go.mod | Local previews with production configuration |
When only editing content, use the PowerShell equivalents:
| Goal | PowerShell |
|---|---|
| Everyday preview | hugo server |
| Production build | hugo --cleanDestinationDir --minify |
| Preview close to production | hugo server --environment production --minify |
dev prioritizes fast visual feedback; check takes longer and runs automated tests. When changing the theme, first get the result right with dev, then pass check.
Publishing: three Git steps
After confirming the local preview:
git add: selects files for this commit. Specify the exact path when committing only the blog post; usegit add -Ato stage everything.git commit: creates a local snapshot.git push: sends it to GitHub and triggers Actions deployment.
The order is add → commit → push.
Summary
The main idea is: Hugo generates pages, Oink controls their appearance, and the content/ folders form the navigation tree. Writing a blog post means adding front matter and Markdown, previewing with hugo server, and pushing. Put {caption=...} on its own line, and convert Obsidian’s == and [[links]] to standard Markdown before publishing.
For a more systematic introduction, read chapters 1–3 of the Oink tutorial book. This site’s RAICOM documentation is a writing example in Chinese.