Quickstart
From an empty folder to a working site
A basic site needs a home page, a header, a footer, and maybe a page or a section. This page goes through each of them in order. All you need is ksh, awk and, to look at the result, python3.python3 is only used by ./serve.sh. Building needs nothing but ksh and awk.
Requirements
awkward assumes OpenBSD, where everything it needs to build a site comes with the base system. It is portable to any other POSIX system that has ksh installed. Nothing else has to be installed, apart from python3 if you want ./serve.sh to look at the site locally.
Get awkward
Every site built with awkward is a fork of it, so start by forking the repository and cloning your fork. All the commands below are run from its root.
The fork comes with this example site in content. Keep it around as a reference while you write, and delete the example pages and the essays section once your own content is in place.
Configure the site
Open config.sh and set at least the name of the site and its author:
SITE_TITLE="My site"
SITE_AUTHOR="Jane Doe"
BASE_URL="https://example.org"
BASE_URL is only used for canonical links and social media tags, so it can stay as it is until the site is published.
Write the home page
The only file a site must have is content/index.md. It starts with frontmatter, between two --- lines on the very first line of the file, in which title and description are required:
---
title: My site
subtitle: Notes on things I like
description: The personal site of Jane Doe.
---
[Welcome to my site]{.newthought}, where I write about things I like.
The title becomes the heading of the page, and the description is used by search engines.
Build and look at it
Build the site and serve it:
./build.sh
./serve.sh
The site is now at http://localhost:8000/. ./build.sh writes the site into target and stops with an error, naming the file and line, if anything is wrong.A failed build leaves the last good site in target untouched, and its intermediate files in work to look at. Run it again after every change.
Add a page
Standalone pages, such as an about page, go in content/pages. The file name becomes both the page's name and its URL, so content/pages/about.md is the page about, built as /about/:
---
title: About
description: Who writes this site.
---
This site is written by Jane Doe.
Add a header and a footer
content/header.md holds the links in the header, one per line. A link points to a page or section by its name:
- [Notes](notes)
- [About](about)
content/footer.md is a little markdown of its own, with paragraphs, links and a list:
© 2026 Jane Doe. Built with awkward.
- [About](about)
Both are optional, but a site without a header has no way to get around.
Add a section
A section is a folder in content/sections holding a series of articles, like the essays on this site. Its section.md is the section's main page, which lists the articles below its introduction, newest first:
---
title: Notes
description: Short notes, newest first.
---
Short notes, newest first.
Every other markdown file in the folder is an article. Articles in a listing need a published date:
---
title: A first note
description: The first note on this site.
published: 2026-10-06
---
[This is the first note]{.newthought} on my site.
Saved as content/sections/notes/first-note.md, it is built as /notes/first-note/ and can be linked to as notes/first-note. Images used in the section go in its images folder, and are written as images/name.png.
A page with draft: yes, or a published date in the future, is not built. To look at such pages locally, build with SHOW_DRAFTS=yes ./build.sh.
Publish
Set PUBLISH_DEST in config.sh to an rsync destination, such as user@host:/var/www/htdocs/site/, and run ./publish.sh. It publishes exactly what is in target, the same site you looked at with ./serve.sh.
Back up and move the site
Everything that makes up the site itself, config.sh, content and static, can be saved as a single compressed archive:
./archive.sh my-site-2026-10-07
This writes my-site-2026-10-07.tar.gz; without a name, ./archive.sh asks for one. Keep the archive somewhere outside the repository, as a long-term backup of the content.Unlike the backups build.sh makes of target, which clean.sh removes after a while, an archive is only the sources, and stays until you delete it.
To go back to an archive, or to move the site to a fresh fork of a newer awkward, restore it from the root of the repository and build:
./restore.sh ~/my-site-2026-10-07.tar.gz
./build.sh
Restoring replaces config.sh, content and static with those in the archive. The ones it replaces are first archived into backups, so a restore of the wrong archive can be undone the same way.The archive also holds the stylesheets in static, so after moving to a newer awkward, check whether its tufte.css and awkward.css should be copied over the restored ones.
From here, Writing with awkward shows all the markdown awkward understands, and about explains where things live.