foliate

A minimal static site generator that turns a markdown vault (e.g., Obsidian notes) into a static HTML website.
Why
I’ve been keeping a collection of markdown notes that include research ideas, paper summaries, reading notes, how-tos, or blog-like posts (whatever you call it—a wiki, Zettelkasten, hypercard, … see Ideas of connected ideas). This works as a central place for my personal knowledge.
At the same time, I have been managing my homepage using Jekyll and GitHub Pages. This is a fine solution, but there has always been this nagging friction. It arises when I try to publish some of my pages in my wiki: should I copy the file to my homepage and publish as blog? But then this creates duplicates. Do I create a totally separate wiki site just for these? I did that, but it’s annoying to manage two totally separate system as my “homepage”.
So I always wanted to have a more integrated solution for this issue—a system for publishing both my homepage and wiki pages.
There are some existing options:
- Obsidian Publish: It looks great, but it’s tied to Obsidian and you need to pay. Because it’s about publishing vault, rather than “homepage”, I think inherently its customizability is a bit limited.
- Hugo / Jekyll / Eleventy / …: Full-featured SSGs, but they assume blog-centric structures and require frontmatter scaffolding that doesn’t match how a wiki-style vault is organized. You end up reshaping your notes to fit the tool.
What I wanted is a unified content management (markdown files + obsidian) and a tool that I can simply point at my existing vault, tell it which pages are public, and get a website. No restructuring. No copying files into a separate project. And the vault acting as the site source. Update a markdown file, I rebuild, and it’s live.
That’s foliate.
Key ideas
- Everything lives in the vault: Config, templates, build output—all inside a
.foliate/directory in your vault. You can sync via git or any other mechanisms. No separate project to maintain. - Two-tiered visibility: Pages are private by default. Add
public: trueto make a page URL-accessible. Addpublished: trueto also list it in indexes and search. This means you can share a link to a draft without it appearing in listings. - Dual deployment: A special
_homepage/directory deploys to the site root (/about/,/projects/). Everything else goes under/wiki/. One vault, two URL hierarchies. - Incremental builds: Only changed files are rebuilt. Config or template changes trigger a full rebuild automatically.
- Obsidian-compatible: Supports wikilinks, Obsidian image syntax (
), and KaTeX math. Private wikilinks are sanitized to plain text in output. - Customizable without fighting: Override any template or stylesheet by placing files in
.foliate/templates/or.foliate/static/. Defaults are bundled and sensible.
Installation
uv tool install foliate
Or run it directly without installing:
uvx foliate build
Quick start
From inside your vault:
uvx foliate init # creates .foliate/config.toml
uvx foliate watch # watch changes, rebuild, and serve locally
uvx foliate build # generates site to .foliate/build/
That’s it. Edit .foliate/config.toml to set your site name, URL, and navigation. Mark pages as public by adding frontmatter:
---
public: true
published: true
---
Then rebuild.
Visibility system
Foliate’s visibility model is intentionally simple:
| Frontmatter | Result |
|---|---|
| (nothing) | Private. Not built. |
public: true |
Built. Accessible via direct URL. Hidden from listings. |
public: true, published: true |
Built. Visible in listings, search, and feed. |
This lets you share work-in-progress pages by link without cluttering your public index.
Directory conventions
Special directories use an underscore prefix:
| Directory | Behavior |
|---|---|
_homepage/ |
Content deployed to site root (/) instead of /wiki/ |
_private/ |
Always ignored, regardless of frontmatter |
Everything else — markdown files, nested folders, whatever structure you already have — maps to /wiki/Your/Path/.
Configuration
.foliate/config.toml controls everything. Key sections:
[site]
name = "My Wiki"
url = "https://example.com"
author = "Your Name"
[build]
wiki_prefix = "wiki" # URL prefix for wiki content
home_redirect = "about" # Where / redirects to
ignored_folders = ["_private"] # Folders to skip
[nav]
items = [
{ url = "/about/", label = "About" },
{ url = "/wiki/Home/", label = "Wiki" },
]
[feed]
enabled = true
items = 20
window = 30 # days to include
Atom feed
Foliate generates an Atom feed at /feed.xml for published wiki content. It distinguishes between new pages (individual entries) and recently updated pages (a single digest entry). Configure the time window and entry count in [feed].
Deployment
Built-in deployment to GitHub Pages:
foliate deploy # rsync to target repo, commit, push
foliate deploy --dry-run # preview without executing
Configure in [deploy]:
[deploy]
method = "github-pages"
target = "../my-site.github.io"
exclude = ["CNAME", ".gitignore"]
Watch mode
For local development:
foliate watch
This builds, starts a local server, and auto-rebuilds on file changes.
CLI reference
foliate init # Create .foliate/config.toml
foliate build # Build site
foliate build --force # Force full rebuild
foliate build --serve # Build + local server
foliate watch # Build + serve + auto-rebuild
foliate deploy # Deploy to configured target
foliate deploy --dry-run # Preview deployment
foliate clean # Remove build/ and cache/