Introduction
Your blog at blog.adarshrust.com is a static site generated by Eleventy and deployed by GitHub Actions to GitHub Pages.
The entire publishing workflow is:
- Open github.com/Webrowse/adarshrust-blog.
- Navigate to
content/YYYY/MM/. - Create or edit a Markdown file.
- Commit.
- GitHub Actions builds and deploys. The article is live in about a minute.
There is no CMS, no admin dashboard, no database, and no local setup required to publish. Everything on the site — homepage, archive, tag pages, RSS, sitemap, search, related articles — is regenerated from your Markdown files on every build.
The mental model
Think of the repository as the blog:
| You do | The build does |
|---|---|
| Add a Markdown file | Creates the article page, adds it everywhere it belongs |
Edit frontmatter tags: | Updates the tag pages and tag cloud |
Set published: false | Removes the article from the entire site |
Set a future date: | Hides the article until that day |
| Delete the file | Removes the article and every reference to it |
You never edit HTML, never update an index page, and never touch configuration to publish.
Where things live
adarshrust-blog/
├── content/ ← articles, organized by year/month
│ └── 2026/
│ └── 05/
│ ├── hello-world.md
│ └── images/
├── pages/ ← standalone pages (/about/, /now/, …)
├── _includes/ ← HTML layouts (rarely touched)
├── _data/site.js ← site title, URL, author
├── eleventy.config.js ← the build logic
└── .github/workflows/deploy.yml ← build + deploy pipeline
The rest of this book walks through each part of the workflow.
Publish your first article
This chapter walks through publishing entirely from the GitHub website.
1. Create the file
-
Go to your repo and press
.— or navigate to content/ and click Add file → Create new file. -
In the filename box, type the path and name in one go:
content/2026/07/building-romyq.mdTyping
/in the filename box creates folders, so a new month needs no separate step.The filename becomes the URL slug: this file will publish at
/2026/07/building-romyq/. Use lowercase words separated by hyphens.
2. Write the article
Paste this skeleton and edit it:
---
title: Building Romyq
description: Why I built an autonomous project manager.
date: 2026-07-12
tags:
- rust
- ai
---
The article body starts here, in plain Markdown.
Only title, description, date, and tags are needed for a normal post. The full list of fields is in the Frontmatter reference.
3. Commit
Click Commit changes. Commit directly to main — every push to main triggers a build and deploy.
4. Watch it go live
Open the Actions tab. The Deploy workflow takes roughly a minute. When it turns green, the article is live at:
https://blog.adarshrust.com/2026/07/building-romyq/
It will also automatically appear on the homepage, in /archive/, on its tag pages, in the RSS feed, the sitemap, and the search index.
Editing an existing article
Open the file on GitHub, click the pencil icon, edit, commit. Same pipeline, same result. Every article page also has an Edit on GitHub link in its footer that jumps straight to the right file.
If you make a meaningful update, set updated: 2026-07-15 in the frontmatter so readers (and search engines) see the revision date.
Frontmatter reference
Frontmatter is the YAML block between --- lines at the top of every article.
---
title: Building Romyq
description: Why I built an autonomous project manager.
date: 2026-07-12
updated: 2026-07-15
tags:
- rust
- ai
- productivity
published: true
cover: ./images/cover.webp
---
Fields
| Field | Required | What it does |
|---|---|---|
title | yes | Article title. Shown on the page, cards, feeds, and <title>. |
description | yes | One-or-two-sentence summary. Used on cards, meta description, OpenGraph, and search results. |
date | yes | Publication date (YYYY-MM-DD). Controls ordering everywhere. A future date keeps the article hidden until that day. |
updated | no | Last-revision date. Shown next to the publish date and used in JSON-LD / sitemap. |
tags | no | List of plain strings. Each tag automatically gets a page at /tags/<tag>/. |
published | no | Defaults to true. Set false and the article is never built — it appears nowhere on the production site. |
cover | no | Path to a cover image relative to the article, e.g. ./images/cover.webp. Shown on the article, on cards, and used as the OpenGraph image. |
Rules worth remembering
- The date comes from frontmatter, never from the filename or folder. The folder (
content/2026/07/) only determines the URL. Keep them consistent for sanity, but changingdate:does not move the URL — so links never break. - Tags are just strings. Reusing an existing tag (check
/tags/) groups articles together; a new string creates a new tag page automatically. - Keep descriptions real. They are what people see on the homepage, in search results, and when the link is shared.
titleanddescriptioncontaining a colon should be quoted:title: "Rust: the good parts".
Writing in Markdown
Articles are standard Markdown — the same dialect you use in GitHub READMEs. There is no custom syntax to learn.
Supported features
Headings
## Section
### Subsection
Start body headings at ## (the article title is the #). Every heading gets an anchor link (hover to see the #), and articles with three or more ##/### headings get an automatic table of contents.
Code blocks
Fenced blocks with a language get build-time syntax highlighting and a Copy button:
```rust
fn main() {
println!("hello");
}
```
Tables
| Flag | Meaning |
|------|---------|
| `-v` | verbose |
Task lists
- [x] write the draft
- [ ] add benchmarks
Quotes
> Simple is robust.
Footnotes
Rust's borrow checker helps here.[^1]
[^1]: Most of the time.
The footnote body can go anywhere in the file; footnotes render at the bottom of the article.
Links and images
[link text](https://example.com)
[another article](/2026/05/hello-world/)

Link to your own articles with their site-absolute path (/2026/05/hello-world/). Images are covered in the next chapter.
What happens automatically
- Reading time is computed from word count.
- The table of contents builds itself from your headings.
- Code blocks get a language label and Copy button.
- The article’s full text becomes searchable via the site search (⌘K).
You never need HTML, shortcodes, or layout tweaks inside an article.
Images
Images live in an images/ folder beside the articles of that month:
content/2026/07/
├── building-romyq.md
├── rust-career-path.md
└── images/
├── cover.webp
├── architecture.png
└── demo.gif
Uploading on GitHub
Navigate to the month folder (e.g. content/2026/07/), click Add file → Upload files, and drop the images in. If the images/ folder does not exist yet, upload from inside the month folder and GitHub will keep the paths when you drag a folder in — or create a file named images/.gitkeep first to create the folder.
Referencing images
In the article, use a plain relative path:

The build resolves it to the deployed location (/2026/07/images/architecture.png) everywhere the article appears — the page itself, the RSS feed, everything. ./images/... works too.
The alt text (inside ![...]) matters: write what the image shows.
Cover images
Set a cover in frontmatter with the same relative style:
cover: ./images/cover.webp
The cover shows at the top of the article, on its homepage card, and becomes the image used when the link is shared on social media (OpenGraph/Twitter card). A wide image around 1200×630 works best.
Practical tips
- Prefer
.webpfor photos and screenshots — much smaller than PNG at the same quality. - Keep images under ~300 KB where you can; every reader downloads them.
- Images are shared per month, so several articles in
2026/07/can reference the sameimages/folder.
Drafts and scheduled publishing
Both features use frontmatter only. There are no draft folders and no separate branches needed.
Drafts: published: false
---
title: Half-formed thoughts on async
description: Not ready yet.
date: 2026-07-20
published: false
---
An article with published: false is not built at all: no HTML page exists, and it appears in no listing, feed, sitemap, or search index. It is only visible as a file in the repository.
To publish it later, change the value to true (or delete the line) and commit.
Note: the repository itself is public, so a draft is hidden from the site, not from someone browsing the repo.
Scheduling: a future date:
---
title: Announcing Romyq 1.0
description: The launch post.
date: 2026-08-01
---
If date is in the future at build time, the article stays hidden — same treatment as a draft. You can write and commit launch posts days in advance.
Two things make the article appear when the day arrives:
- The daily scheduled build. GitHub Actions rebuilds the site every day at 00:30 UTC (06:00 IST), so a scheduled article goes live on its date without any action from you.
- Any other push. Every deploy re-evaluates all dates, so pushing anything after the date also releases it.
If you want a scheduled article out immediately rather than at the next daily build, trigger a deploy by hand: Actions → Deploy → Run workflow.
Checking what is pending
Hidden articles are simply files whose frontmatter says “not yet”. To review them, search the repo for published: false, or list files under content/ with dates ahead of today.
What is generated for you
Every build regenerates the whole site from the Markdown files. Nothing below is ever maintained by hand.
| Feature | Where |
|---|---|
| Homepage, newest first, paginated by 10 | /, /page/2/, … |
| Archive grouped by year and month | /archive/ |
| Month listings | /2026/07/, … |
| Tag index and per-tag pages (paginated by 20) | /tags/, /tags/rust/, /tags/rust/page/2/ |
| RSS feed (20 newest, full content) | /rss.xml |
| Sitemap | /sitemap.xml |
| Search index | /search-index.json |
| Search UI | /search/, or ⌘K / Ctrl-K anywhere |
| 404 page | any missing URL |
On each article page
- Reading time, from word count.
- Table of contents, when the article has 3+ headings.
- Heading anchors for deep-linking to sections.
- Previous / Next links to the adjacent articles by date.
- Related posts — up to three articles sharing the most tags.
- Copy URL button and Edit on GitHub link.
- Reading progress bar at the top of the viewport.
In the page metadata (invisible but important)
- Canonical URL.
- OpenGraph and Twitter card tags (title, description, cover image) — these control how links look when shared on social media and chat apps.
- JSON-LD
BlogPostingschema for search engines.
Search
Search is client-side: the browser fetches /search-index.json (title, description, tags, and article text) and ranks matches locally, title and tag hits scoring highest. No server, no external service. The index is rebuilt on every deploy, so new articles are searchable as soon as they are live.
URLs and file layout
The rule
content/YYYY/MM/slug.md → https://blog.adarshrust.com/YYYY/MM/slug/
The URL comes from where the file sits, and the slug from the filename. The frontmatter date orders the article but does not affect the URL. That split is deliberate: you can correct a date without breaking every link to the article.
Choosing filenames
The filename should simply describe the article:
content/2026/07/building-romyq.md
content/2026/07/rust-career-path.md
content/2026/07/life-in-bangalore.md
- Lowercase, hyphen-separated words.
- No dates in the filename — the folders already carry year and month.
- Filenames must be unique within their month (they can repeat across months, since the URL includes
YYYY/MM/).
Keep URLs stable
Once an article is published, its URL is a promise. Renaming or moving the file changes the URL and breaks inbound links, feeds readers already fetched, and search engine entries. Fix typos in the title: freely — it does not touch the URL. Only rename a file if the article is very fresh or you accept the breakage.
Growing over time
New year or month? Just include it in the path when creating a file — content/2027/01/first-post.md — and the folder, the month listing (/2027/01/), and the archive entry all come into existence on their own. Empty months simply don’t exist; there is nothing to pre-create and nothing to clean up.
The structure scales to thousands of posts: each month stays a small folder, and every page of the site is regenerated from scratch on each build, so nothing drifts.
Standalone pages
Pages that aren’t dated articles — About, Now, Uses — live in pages/:
pages/about.md → /about/
pages/now.md → /now/
pages/uses.md → /uses/
pages/todo.md → /todo/
The URL is just the filename. Frontmatter is minimal:
---
title: About
description: About Adarsh, a Rust developer in India.
---
Page body in Markdown.
Pages do not appear on the homepage, in the archive, in tag pages, or in the RSS feed — they exist only at their own URL (and in the sitemap). Editing them works exactly like editing an article: change the file on GitHub, commit, wait for the green check.
The todo page
pages/todo.md is a special case: it declares layout: layouts/todo.njk in its frontmatter, which adds interactive checkboxes backed by your browser’s localStorage and a Clear All button. Write it as a normal Markdown task list:
### Morning
- [ ] Duolingo
- [ ] Review PRs
Checkbox state lives only in the browser that checked them; the Markdown file stays untouched.
Adding a new page
Create pages/talks.md with a title and description, commit, and /talks/ exists. If you want it in the site header next to Archive / Tags / About, add one line to the nav in _includes/layouts/base.njk — the only case where a new page touches a template.
Local development
You never need to run the site locally to publish. But for template changes, CSS work, or previewing a long article, it’s one command away.
Setup (once)
git clone git@github.com:Webrowse/adarshrust-blog.git
cd adarshrust-blog
npm install
Requires Node 20+ (the deploy uses Node 24).
Preview with live reload
npm run serve
Serves at http://localhost:8080 and rebuilds on every file save — edit a Markdown file and the browser refreshes itself.
Because future-dated articles are excluded at build time, a scheduled post won’t show locally either. To preview one, temporarily set its date to today (and remember to set it back before committing, or commit it — it just publishes early).
One-off build
npm run build
Writes the complete site to _site/. That folder is exactly what gets deployed; you can open pages from it directly or serve it with any static file server.
Typical local workflows
- Previewing a draft: run
npm run serve, write in your editor, watch the rendered article. Commit when happy. - Styling: all CSS is in
static/css/(tokens.cssholds the colors and fonts;components.cssmost of the look). Plain CSS, no build step. - Template changes: layouts are Nunjucks files in
_includes/. The dev server reloads them like everything else.
_site/ and node_modules/ are gitignored — never commit them.
Deployment and troubleshooting
The pipeline
git push (or daily 00:30 UTC cron, or manual trigger)
↓
GitHub Actions: npm ci && npm run build
↓
Upload _site/ as a Pages artifact
↓
Deploy to GitHub Pages
↓
https://blog.adarshrust.com
Defined in .github/workflows/deploy.yml. Three triggers:
- push to
main— every commit deploys. - schedule (daily 00:30 UTC) — releases future-dated articles.
- workflow_dispatch — manual: Actions → Deploy → Run workflow.
One-time setup (do this if not done yet)
- Repo Settings → Pages → Build and deployment → Source: GitHub Actions.
- Same page: set Custom domain to
blog.adarshrust.comand keep Enforce HTTPS on once available. - At your DNS provider: point
blog.adarshrust.comwith a CNAME record towebrowse.github.io(replacing the old Railway record). - Decommission the Railway service once the new site answers.
Until step 1 is done, the workflow’s deploy job fails with a “Pages not enabled” style error — the build job still proves your content is fine.
When something goes wrong
The Actions run is red. Open the run and read the failing step.
- build failed → almost always broken frontmatter. YAML is picky: a stray tab, an unquoted colon in a title (
title: "Rust: notes"needs quotes), or mismatched---fences. The log names the file. - deploy failed → a Pages/permissions problem, not your article. Check the one-time setup above; re-run the job from the Re-run jobs button.
The run is green but the article is missing. Check, in order:
date:— is it in the future?published:— is itfalse?- Is the file under
content/with a.mdextension?
An image is broken. The path in the Markdown must match the file’s location relative to the article — usually images/name.png, and case-sensitive in production even if it works on your Mac.
The site looks stale. Deploys finish in ~1 minute but your browser may cache pages; hard-refresh (⌘⇧R) before debugging further.
Scheduled post didn’t appear. GitHub pauses cron schedules on repos with no activity for 60 days. Any push, or a manual Run workflow, resumes it and releases the post.
How the build works
You can run the blog for years without reading this chapter. It’s here for when you want to change behavior, not content.
The two files that matter
content/content.11tydata.js
Data applied to every file under content/. This is where the publishing rules live:
- permalink — built from the file’s path (
content/2026/07/x.md→/2026/07/x/), orfalse(don’t build) when the article isn’t live. - isLive — an article is live unless
published: falseor itsdateis in the future. - coverUrl / editUrl — computed per article for templates.
eleventy.config.js
Everything else, in one file (~250 lines):
| Section | Does |
|---|---|
| Markdown setup | markdown-it + anchors, footnotes, task lists |
| Collections | posts (all live articles, newest first), tagPages (tag × page-of-20 chunks), archiveMonths |
| Filters | dates, reading time, related-posts scoring, TOC extraction, search index JSON |
| Preprocessor | rewrites  to the article’s absolute path before rendering |
| Feed plugin | generates /rss.xml |
| Syntax highlighting | Prism at build time |
| Passthrough | static/ → site root; content/**/images/ → /YYYY/MM/images/ |
Templates
Nunjucks files, thin by design:
_includes/layouts/base.njk ← <head>, meta tags, header, footer, search overlay
_includes/layouts/post.njk ← article page: TOC, body, prev/next, related
_includes/layouts/page.njk ← standalone pages
_includes/partials/post-card.njk ← the card used on every listing
index.njk / archive.njk / month.njk / tags.njk / tag.njk / search.njk / 404.njk
sitemap.njk / search-index.njk ← XML and JSON outputs
Site-wide values (title, URL, author, GitHub links) are in _data/site.js and available in every template as site.*.
Design constraints to preserve
- Convention over configuration. The file’s location is its URL; the frontmatter is its metadata. Resist adding options.
- Publishing must never require code. If a new feature would make authors touch templates per-article, it’s the wrong design.
- Build-time only. No client-side rendering of content, no external services. The one exception is the small vanilla-JS layer for theme, search, and copy buttons in
static/js/.