| name | marmite |
| description | Build and manage static sites with marmite - a zero-config static site generator that turns markdown files into websites |
Marmite Static Site Generator
Marmite is a static site generator written in Rust that converts a folder of markdown files into a complete website. It requires zero configuration to get started - just point it at a folder with .md files and it produces HTML.
Installation
curl -sS https://marmite.blog/install.sh | sh
pip install marmite
uvx marmite
cargo install marmite
cargo binstall marmite
brew install marmite
docker run --rm -v $(pwd):/input ghcr.io/rochacbruno/marmite
iwr -useb https://marmite.blog/install.ps1 | iex
See references/installation.md for all install methods, custom directories, and troubleshooting.
Essential Commands
marmite <input_folder> [output_folder]
marmite <input_folder> --serve --watch
marmite <folder> --init-site
marmite <folder> --generate-config
marmite <folder> --new "My Post Title"
marmite <folder> --new "About" -p
marmite <folder> --new "My Post" -t "rust,web" -e
marmite <folder> --init-templates
marmite <folder> --start-theme mytheme
marmite <folder> --set-theme https://github.com/user/marmite-theme
marmite --skill-install
marmite --skill-install-claude
marmite --skill-install --skill-install-claude
marmite <folder> atproto auth
marmite <folder> atproto publish
Workflow: Start a New Project
mkdir mysite && cd mysite
marmite . --init-site
marmite . --serve --watch
This creates:
mysite/
content/ # Markdown files go here
marmite.yaml # Site configuration
site/ # Generated output (default)
If you already have markdown files in a folder, just run marmite <folder> with no setup needed.
Workflow: Content Authoring
Before writing content - consult site metadata
Before creating or editing content, read the site's build metadata to understand what already exists. This prevents tag typos, slug collisions, orphaned streams, and inconsistent author names.
The output folder defaults to site/ inside the input folder, but can be overridden by the second CLI argument (marmite <input> <output>). Check marmite.yaml for a site_path override, or look for the site/ directory. All paths below use <output>/ to mean whichever output folder the site uses.
<output>/marmite.json - the full site manifest (always generated). Read this file first:
posts / pages - every content item with its slug, URL, date, tags, authors, stream, series, description, and pinned status. Use this to check existing slugs, reuse exact tag names, and match established patterns.
config - the active site configuration including authors (configured profiles), streams (display names), series (display names and descriptions), menu, language, and extra settings.
shortcodes - list of available shortcode names.
<output>/urls.json - all generated URLs grouped by type (enabled by default via publish_urls_json: true). Useful for:
- Checking which tag/author/stream/series pages exist.
- Counting content totals via the
summary section.
- Verifying a slug won't collide with an existing URL.
You can also get the same URL data without a build via the CLI: marmite <input_folder> --show-urls.
What to check before writing:
- Tags - read
marmite.json posts to see existing tag names. Reuse them exactly (e.g., use rust not Rust if the site uses lowercase).
- Authors - check
config.authors for configured profiles and config.default_author for the fallback.
- Streams - check
config.streams for defined streams and look at existing posts' stream values.
- Series - check
config.series and existing posts' series values before assigning content to a series.
- Slugs - scan existing slugs in
posts and pages to avoid collisions. Also check urls.json redirects.
- Date format - look at existing posts' dates to match the pattern.
- Description style - look at existing descriptions for length and tone consistency.
Posts vs Pages
Content with a date is a post (appears in feeds, index, archive, search). Content without a date is a page (standalone, accessible by direct link or menu).
Dates can come from the filename or frontmatter:
content/2024-06-15-my-post.md
content/my-post.md
content/about.md
Frontmatter
Three formats are supported. YAML is recommended:
---
title: "My Post Title"
date: 2024-06-15
slug: custom-url-slug
tags: rust, web, tutorial
authors: alice, bob
description: "A short description for SEO and feeds"
series: my-tutorial-series
stream: tutorial
pinned: true
toc: true
card_image: media/social-card.jpg
banner_image: media/banner.jpg
extra:
math: true
mermaid: true
---
TOML (+++ delimiters) and JSON ({} wrapper) are also supported. See references/frontmatter.md for the full field reference.
Folder-Level Frontmatter Defaults
A frontmatter.yaml file in a content subfolder provides default values for all .md files in that folder. Works at any nesting depth with layered inheritance - deeper folders inherit from ancestors and can override specific fields. Per-file frontmatter always wins. title and slug are never inherited.
content/
frontmatter.yaml # Root-level defaults (apply to all content)
tutorials/
frontmatter.yaml # Inherits from root, adds stream: tutorial
rust/
frontmatter.yaml # Inherits from tutorials, adds tags
intro.md # Gets all three layers of defaults
See references/frontmatter.md and references/content-organization.md for details.
Redirect Aliases
The aliases frontmatter field generates redirect pages at old URLs when content slugs change:
aliases: old-post-url, legacy-path
Each alias generates a lightweight HTML file with <meta http-equiv="refresh">, a canonical link, and a JS fallback pointing to the current URL. Redirect pages are excluded from sitemap, feeds, and search. A warning is logged if an alias conflicts with an existing slug.
Creating Content via CLI
marmite . --new "Getting Started with Rust"
marmite . --new "About Me" -p
marmite . --new "Rust Tips" -t "rust,tips" -e
Taxonomy
Tags - group content by topic:
tags: rust, web, tutorial
Generates: /tags.html, /tag-rust.html, /tag-rust.rss
Authors - group by author:
authors: alice
Generates: /authors.html, /author-alice.html
Author profiles are configured in marmite.yaml:
authors:
alice:
name: Alice Smith
avatar: https://example.com/alice.png
bio: "Rust developer"
links:
- ["Github", "https://github.com/alice"]
Streams - separate content categories:
stream: tutorial
Generates: /tutorial.html, /tutorial.rss, /streams.html
Special stream draft hides posts from main feeds while keeping them accessible by URL.
Configure display names in marmite.yaml:
streams:
tutorial:
display_name: "Tutorials"
Language Streams - multilingual content (auto-detected from content, config optional):
language: en
languages:
en:
display_name: "English"
pt:
display_name: "Portugues"
Set language: xx in frontmatter or use subfolder naming conventions. Link translations via subfolder auto-discovery, translations: list, or translates: pointer. Each language gets its own stream page and RSS feed. Translation links and hreflang SEO tags are added automatically. A languages.html group page lists all content by language (always generated, configurable via languages_title).
See references/content-organization.md for all content organization modes.
Series - multi-part ordered content:
series: python-tutorial
Generates: /serie-python-tutorial.html, /series.html
Posts in a series get automatic prev/next navigation. Configure in marmite.yaml:
series:
python-tutorial:
display_name: "Python Tutorial"
description: "Learn Python step by step"
Markdown Features
Marmite supports extended markdown:
- Tables, strikethrough, task lists, footnotes
- Wikilinks:
[[page-slug]] or [[Display Text|page-slug]]
- Alerts:
> [!NOTE], > [!WARNING], > [!TIP]
- Spoilers:
||hidden text||
- Description lists, underline, multiline block quotes (
>>>)
- Math (when
extra.math: true): $inline$ and $$display$$
- Mermaid diagrams (native SVG via
native_mermaid_render: true in config, or client-side JS via extra.mermaid: true in frontmatter)
Workflow: Layout Customization with Fragment Files
Files prefixed with _ in the content directory inject content into template regions without generating their own pages.
| File | Purpose |
|---|
_hero.md | Hero section on the homepage |
_announce.md | Announcement banner |
_header.md | Custom header content |
_footer.md | Custom footer content |
_sidebar.md | Sidebar content |
_comments.md | Comments section (e.g., Giscus script) |
_references.md | Global markdown link references appended to every file |
_htmlhead.md | Raw HTML injected into <head> |
_markdown_header.md | Markdown prepended to every content file |
_markdown_footer.md | Markdown appended to every content file |
_404.md | Custom 404 page |
Example _hero.md:
>>>
Welcome to my blog! I write about Rust and web development.
>>>
Example _references.md:
[Github]: https://github.com/myuser
[docs]: <./tag-docs.html> "Documentation"
Example _comments.md (Giscus):
<script src="https://giscus.app/client.js"
data-repo="user/repo"
data-repo-id="YOUR_ID"
data-category="Announcements"
data-category-id="YOUR_CAT_ID"
data-mapping="pathname"
crossorigin="anonymous"
async>
</script>
Workflow: Configuration
Create or edit marmite.yaml in the project root. Key options:
name: "My Blog"
tagline: "A blog about things"
url: "https://myblog.com"
language: "en"
pagination: 10
enable_search: true
toc: true
menu:
- ["Home", "index.html"]
- ["Tags", "tags.html"]
- ["Archive", "archive.html"]
- ["About", "about.html"]
default_author: myuser
default_date_format: "%B %d, %Y"
extra:
colorscheme: dracula
colorscheme_toggle: true
colormode: dark
See references/config-reference.md for the complete list of all configuration options.
Workflow: Template Customization
Marmite uses the Tera template engine (Jinja2-like syntax).
marmite <folder> --init-templates
This creates a templates/ directory with all template files. The key templates:
| Template | Purpose |
|---|
base.html | Base layout (all pages extend this) |
content.html | Single post/page view |
list.html | Content listings (index, tag pages, etc.) |
group.html | Grouped content (tags overview, streams overview) |
Template blocks in base.html:
{% block seo %} - Open Graph and meta tags
{% block head %} - CSS and head elements
{% block main %} - Main content area
{% block tail %} - Scripts at end of body
Key template variables:
site - The full site configuration object
site.name, site.tagline, site.url, site.extra
menu - Navigation menu items
content - Current post/page object (on content pages)
content_list - Array of posts (on list pages)
hero, sidebar, header, footer, announce - Fragment content
Custom Tera functions available:
url_for(path="page.html", abs=false) - Generate URLs
group(kind="tag", ord="desc", items=0) - Group content
get_posts(ord="desc", items=10) - Get sorted posts
get_data_by_slug(slug="my-post") - Look up content by slug
source_link(content=content) - Link to markdown source
stream_display_name(stream="tutorial") - Get display name
series_display_name(series="my-series") - Get display name
Custom filters:
{{ content.date | default_date_format }} - Format dates
{{ items | remove_draft }} - Filter out draft content
See references/tera-templates.md for the full template reference.
Workflow: Theme Creation
marmite <folder> --start-theme mytheme
This creates:
mytheme/
templates/
base.html
content.html
list.html
group.html
static/
style.css
script.js
custom.css
custom.js
favicon.ico
colorschemes/
theme.json
README.md
Edit theme.json for theme metadata:
{
"name": "My Theme",
"version": "0.1.0",
"author": "Your Name",
"description": "A custom marmite theme",
"license": "MIT",
"marmite_version": ">=0.3.0"
}
Activate the theme in marmite.yaml:
theme: mytheme
Install a remote theme:
marmite <folder> --set-theme https://github.com/user/marmite-theme-name
Available built-in colorschemes: catppuccin, clean, dracula, github, gruvbox, iceberg, minimal, minimal_wb, monokai, nord, one, solarized, typewriter.
Workflow: Shortcodes
Shortcodes are reusable content blocks. Default syntax uses HTML comments:
<!-- .youtube id=dQw4w9WgXcQ -->
<!-- .posts items=5 -->
<!-- .tags ord=asc -->
<!-- .gallery path=photos width=200 -->
Built-in shortcodes: youtube, spotify, posts, pages, tags, streams, authors, series, card, gallery, toc, socials.
Creating Custom Shortcodes
Place .html or .md files in the shortcodes/ directory.
HTML shortcode (shortcodes/alert.html):
{% macro alert(type="info", message="") %}
<div class="alert alert-{{ type }}">{{ message }}</div>
{% endmacro alert %}
Usage: <!-- .alert type=warning message=Be careful! -->
Markdown shortcode (shortcodes/note.md):
> **{{ title | default(value="Note") }}**: {{ content }}
See references/shortcodes.md for the full shortcode reference.
Workflow: Image Optimization
Marmite automatically resizes images during build to optimize page load times.
extra:
max_image_width: 800
banner_image_width: 1200
resize_filter: "quality"
Features:
- Parallel processing using all CPU cores
- Incremental builds - unchanged images are cached
- Banner images detected by
.banner. in filename or banner_image frontmatter
- Supports JPEG, PNG, WebP, GIF, AVIF, BMP, TIFF
- Originals preserved - only output copies are resized
Skip during development for faster builds:
marmite <folder> --serve --skip-image-resize
Automatic Image Download
Marmite can auto-download banner images for posts without one:
image_provider: picsum
Downloads a deterministic placeholder image as {slug}.banner.jpg for each post. Only applies to posts, not pages. Delete the downloaded image and rebuild to get a different one.
Workflow: Media Management
Directory Layout
Place media files in content/media/. You can use flat files or organize per-content subfolders:
content/
media/
my-post.banner.jpg # Flat: auto-discovered for slug "my-post"
my-post/ # Subfolder: named after the slug
banner.jpg # Auto-discovered as banner image
card.png # Auto-discovered as card image
diagram.svg # Referenced via @/ in markdown
photo.jpg
2024-06-15-my-post.md
Media can also live inside content subfolders, alongside the markdown files:
content/
my-post/
my-post.md
pt-meu-post.md # Translation
media/
banner.jpg # Shared by all files in the subfolder
card.png
Content subfolder media (content/{slug}/media/) is copied to output/media/{slug}/ and takes precedence over global media (content/media/{slug}/).
Automatic Banner and Card Discovery
Marmite looks for banner and card images in this order:
- Explicit
banner_image / card_image in frontmatter (always wins)
- Flat file:
media/{slug}.banner.{ext} or media/{slug}.{ext}
- Content subfolder media:
content/{slug}/media/banner.{ext}
- Global media subfolder:
content/media/{slug}/banner.{ext}
- Generic subfolder media:
content/{subfolder}/media/banner.{ext} (shared by all files in the subfolder)
- First
<img> in the rendered HTML (card image fallback)
Flat files take precedence over subfolder files, so existing sites are unaffected. A generic banner.jpg in a content subfolder's media directory is inherited by all .md files in that subfolder (useful for translations).
The @/ Shorthand
Use @/ in markdown images and links to reference files in the content's media subfolder. Marmite replaces @/ with media/{slug}/ in src and href attributes of the rendered HTML:

[Download the PDF](@/report.pdf)
For a post with slug my-post, the above becomes src="media/my-post/sunset.jpg" and href="media/my-post/report.pdf".
The replacement only targets HTML attributes, so @/ in plain text, code blocks, and fragment files (_ prefixed) is never touched. The prefix respects the configured media_path.
See references/content-organization.md for full media organization details.
Image Galleries
Marmite has a built-in gallery system for displaying collections of images with thumbnails, navigation, and full-screen viewing.
Setup:
- Create a gallery folder inside
content/media/gallery/:
content/media/gallery/summer2025/
sunset.jpg
beach.jpg
palm-trees.jpg
gallery.yaml # Optional configuration
- Use the gallery shortcode in any post or page:
<!-- .gallery path=summer2025 -->
<!-- .gallery path=summer2025 width=800 height=600 ord=desc -->
Gallery configuration (gallery.yaml, all fields optional):
name: "Summer 2025 Vacation"
ord: asc
cover: "sunset.jpg"
images:
- filename: "sunset.jpg"
description: "Sunset at the beach"
- filename: "*"
description: "Vacation photos"
Description matching supports exact match, partial match, regex patterns, and * catch-all. Matched in order - first match wins.
Config options in marmite.yaml:
gallery_path: "gallery"
gallery_create_thumbnails: true
gallery_thumb_size: 50
Template function for custom gallery layouts:
{% set gallery = get_gallery(path="summer2025") %}
{% for item in gallery.files %}
<img src="media/gallery/summer2025/{{ item.image }}" alt="{{ item.description }}">
{% endfor %}
The gallery interface includes thumbnail strip navigation, keyboard arrow keys, touch/swipe gestures, and responsive design. Image formats: JPG, PNG, WebP, GIF, BMP, TIFF, AVIF.
Workflow: Comments
Add a comment system by creating content/_comments.md:
##### Comments
<script src="https://giscus.app/client.js"
data-repo="youruser/yourrepo"
data-repo-id="YOUR_REPO_ID"
data-category="Comments"
data-category-id="YOUR_CATEGORY_ID"
data-mapping="pathname"
data-theme="preferred_color_scheme"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
Alternatively, configure in marmite.yaml under extra.comments. Disable per-post with comments: false in frontmatter.
See references/comment-system.md for Giscus, Utterances, Hatsu, and other options.
Workflow: Link Checking
Use lychee to validate links in your built site:
marmite <folder> site
lychee --verbose ./site --extensions html
marmite <folder> --serve &
lychee http://localhost:8000
Add to CI with the lycheeverse/lychee-action GitHub Action for automated weekly checks.
IndieWeb Compliance
Marmite's default templates include IndieWeb microformats out of the box:
h-card for author profiles and site identity
h-entry for blog posts and list items
h-feed for content collections
p-category for tags
dt-published for dates
This makes your site machine-readable for feed readers, search engines, and IndieWeb tools. No configuration needed.
For Fediverse verification, add to marmite.yaml:
extra:
fediverse_verification: "https://mastodon.social/@username"
Workflow: Building and Deploying
marmite <input_folder> <output_folder>
marmite <input_folder> --serve --watch
marmite <input_folder> --serve --bind 127.0.0.1:3000
marmite <input_folder> --force
marmite <input_folder> --show-urls
The output is a flat directory of static HTML, CSS, and JS files. Deploy by copying the output folder to any static hosting provider (Netlify, Vercel, GitHub Pages, Cloudflare Pages, or any web server).
Internal Link Validation
Marmite can validate internal links at build time:
check_internal_links: true
strict_internal_links: true
Markdown Source Publishing
Publish the original .md source files alongside HTML output:
publish_markdown_source: true
Each content page gets a link to its markdown source via the source_link(content=content) template function.
File Mapping
Copy arbitrary files into the output during build:
file_mapping:
- source: ai/llms.txt
dest: llms.txt
- source: static/favicon.ico
dest: favicon.ico
Sitemap and Feeds
Generated automatically:
sitemap.xml (when build_sitemap: true, default)
index.rss (always)
index.json (when json_feed: true)
urls.json (when publish_urls_json: true, default)
- Per-tag, per-stream, per-series RSS feeds
Workflow: Workspace Multi-Site
Build multiple independent sites from a single workspace directory:
cat > marmite-workspace.yaml << 'EOF'
defaults:
pagination: 10
enable_search: true
sites:
- name: blog
path: blog
default: true
- name: photos
path: photos
EOF
marmite <workspace> <output>
marmite <workspace> --new "Post Title" --site blog
marmite <workspace> --serve --watch
Workspace mode is activated when marmite-workspace.yaml is found in the input folder. The defaults section accepts any standard marmite.yaml field - each site's own config overrides these defaults.
Cross-site links use site::path syntax: [See gallery](photos::gallery.html) becomes /photos/gallery.html.
See references/config-reference.md and references/cli-reference.md for all workspace options.
Workflow: Content Editor
When serving with --serve, the built-in editor is available at /__marmite__/editor/. Open it from the toolbar's Editor button or navigate directly.
The editor provides:
- Three-panel layout: collapsible metadata sidebar, CodeMirror 6 markdown editor, live preview iframe
- Autocomplete for wikilinks, shortcodes, media paths, and frontmatter
- Auto-save with preview refresh (1.5s debounce)
- Insert menu for common markdown elements and a media file picker
- Config dialog with 9 tabs (including raw YAML editing)
- Raw file editing for fragments, CSS, JS, YAML, and other non-content files
- Project file tree for navigating and opening files
Disable the toolbar and editor with enable_toolbar: false in marmite.yaml or --enable-toolbar false on the CLI.
Workflow: Marmite Playground
Try marmite directly in the browser at marmite.blog/marmite-playground.html. The playground is a live editor where you can write markdown, tweak settings, and preview your site in real time - no installation required.
Reference Files
references/cli-reference.md - Complete CLI flags, options, and command examples
references/installation.md - All installation methods (curl, pip, cargo, brew, Docker, Windows)
references/config-reference.md - Complete configuration options
references/frontmatter.md - Content frontmatter fields
references/content-organization.md - Directory structure, taxonomy, fragment files, and site organization strategies
references/markdown-format.md - Markdown syntax, extensions, wikilinks, math, diagrams, alerts
references/tera-templates.md - Template system, variables, functions, and filters
references/shortcodes.md - Shortcode creation and built-in shortcodes
references/deployment-guide.md - Deploying to GitHub Pages, GitLab, Netlify, Vercel, Cloudflare, Docker, Nginx, Apache
references/comment-system.md - Setting up Giscus, Utterances, Hatsu, and other comment systems
Tools for agent to call
Read site metadata
Before writing content, read the output metadata files to understand the existing site. The output folder defaults to site/ inside the input folder (e.g., ./site/), but can be any path passed as the second CLI argument or set via site_path in marmite.yaml. All paths below use <output>/ as a placeholder.
<output>/marmite.json - full site manifest (always generated):
{
"marmite_version": "0.4.1-dev",
"generated_at": "2026-07-06 18:32:55...",
"elapsed_time": 7.5,
"config": {
"name": "My Blog",
"authors": {"alice": {"name": "Alice", "bio": "..."}},
"streams": {"tutorial": {"display_name": "Tutorials"}},
"series": {"python-tutorial": {"display_name": "Python Tutorial"}}
Use this to:
- Check existing tags, authors, streams, series before writing content
- Verify slug availability
- See the active site configuration and author profiles
- List available shortcodes
<output>/urls.json - all generated URLs by type (when publish_urls_json: true, default):
{
"posts": ["/my-post.html"],
"pages": ["/about.html"],
"tags": ["/tag-rust.html", "/tags.html"],
"authors": ["/author-alice.html", "/authors.html"],
"streams": ["/tutorial.html", "/streams.html"],
"series": ["/series-python-tutorial.html", "/series.html"],
"archives": ["/archive-2024.html", "/archive.html"],
"feeds": ["/index.rss",
Use this to:
- Compare URLs before and after a refactor
- Check generated content slugs
- Count content totals
- Verify no URL collisions
Show urls (CLI alternative)
The same URL data from urls.json is available without a build via:
marmite input_folder --show-urls
init site
marmite input_folder --init-site
Initialize a new site structure from scaffolding.
New post
marmite input_folder --new "title" -t tag
Create a new post.
More
Use marmite --help to check what else is available.