oddy
  1. Blog
  2. How to add a post

How to add a post

Posts live in the Oddy repository as Markdown files. There is no CMS and nothing runs on the server: the site image builds the blog with Zola and nginx serves the files.

Write the file

Create blog/content/<slug>.md. The file name is the address, so this post, blog/content/how-to-add-a-post.md, is at https://blog.oddy.dev/how-to-add-a-post/. Start the file with TOML front matter between +++ lines:

+++
title = "How to add a post"
date = 2026-11-07
description = "One or two sentences for the blog's index and search results."
draft = true

[taxonomies]
tags = ["blog"]
+++
  • title: the heading and the browser tab.
  • date: the publication date. The index lists posts newest first, and the feed only carries posts with a date.
  • description: the summary under the title on the index, and the page description search engines show.
  • draft: true keeps the post out of the build until you set it to false or remove the line.
  • tags: optional. Every tag gets its own page under /tags/.

Everything after the second +++ is the post, in Markdown.

Images

Give a post with images its own folder: blog/content/<slug>/index.md, with the images next to it, and link them by file name: ![A deploy timeline](timeline.png). Images from other sites do not load: the blog's Content Security Policy only allows its own host.

Link to another post by its file, [the first post](@/how-to-add-a-post.md). The build fails when that file does not exist, so links between posts never break.

Preview it

nix-shell blog/shell.nix --run blog/preview.sh

This builds the blog with drafts the way the site image does and serves it at http://127.0.0.1:1111. Run it again after a change. The cookie banner and analytics stay off: they only work on Oddy's own hosts.

Publish it

Merge the post to main. A change under blog/ starts the deploy workflow, which builds the site image with the blog in it and rolls it out. The post is on the index, in the feed at /atom.xml and in the sitemap.