flog - MultiMarkdown-based static site/blog generator


flog [-avbOBP] [-d level] [-C css] [-A author] [-S count] [-T fmt] [-D dir] [-U url] [-p count] [-I count] [-N blogname]


Flog is a static blog generator based on MultiMarkdown. You write the outer bits of your blog and each post in MultiMarkdown. Flog will generate the bits of MultiMarkdown that summarize the pages and posts in the blog and the archives of previous posts. The generated bits are meant to be pulled in to e.g. via MultiMarkdown file inclusion. You can look at for an example of what a Flog-generated site looks like.

Flog generates MultimarkDown, not HTML. It produces files meant to be included in other multimarkdown files via the file inclusion syntax: {{file}}. The site author has the flexibility to decide where these bits of generated content go and how the whole site is structured. Flog takes care of generating the archives, which contains indices into the content by date, title and tag.

Flog also spits out the multimarkdown invocations that should be executed to create the static HTML site. It prints them, one per line, on stdout. This is the usual incantation to completely regenerate your site:

  $ flog | sh

By default the resulting tree will be placed in a subdirectory of the current directory called tmp. You can then tar up ./tmp to the web server docroot, or use the -D option to specify another output directory (lke your docroot).

Command-Line Options

Flog accepts the following options:


Flog generates a directory tree populated with MultiMarkdown files; we call this directory the docroot in what follows. The docroot is based on a set of input files, also multimarkdown, which it finds by various means. The directory structure and naming covention used in the docroot is completely different than that of the input files.

The input files represent one of three kinds of things:

All three of these different kinds of entities are realized in MultiMarkdown. Flog distinguishes among them based on where they are in the input file tree, but it expects the same kinds of metadata in all of them.

By convention, flog expects to be run from the root of your site's source tree, preferably via make(1) using a simple `Makefile` such as that used to generate Flog expect to find all blog posts under posts/, static pages under pages/, books under books/ and papers under papers/.

Every post, page, chapter in a book or section of a paper should have certain basic metadata at the top, separated by a blank line from the content of the posts. There should not be any kind of title or head matter other than the metadata; Flog generates a title and summary for each file on its way to the docroot.


The template I use to start something new looks like this:

    Title: The Title
    name: some-stuff
    Date: 2016-08-05
    Edit: 2016-08-05
    Tags: blah
    draft: 1

    No title here.

Metadata names are always compared in lowercase in flog, but are usually capitalized by convention in multimarkdown.

The draft metadata should be set (to anything) if the file in question is a draft; this means it is ignored under normal circumstances. Drafts can made visible in the docroot with the -d option.

Tags is optional, but if present should be a comma-separated list of tags that apply to the file (whitespace is ignored).

The name metadata is used to form the final filename of the generated multimarkdown file in the docroot. The name of the input file in the filesystem is used as a default if no name key is given.

The title metadata is used in title of the final HTML file. Multimarkdown does this on its own when it generates HTML.

There are two dates that flog cares about: date and edit. The former is the date used to sort blog posts in time (descending). Filesystem metadata such as the mtime of the source file is ignored, only the date metadata matters, so it must be present. The edit date is displayed in summaries. The Time::ParseDate module is used to parse the values, so anything that module accepts can appear as a value (although not all forms make sense in this context, but hey).

There are two other keys that don't appear in the template that have meaning to flog: featured and order. Both take integer values. They will be covered below, as they only apply to certain kinds of inputs.

Blog Posts

Blog posts are ordered backwards by time of publication (the date metadata). They are found by default in the posts subdirectory of wherever flog is run. Flog searches for all *.md files under posts recursively, so it doesn't matter where they are. This allows the site author to organize their posts using the filesystem in whatever way makes sense to them.

In the docroot blog posts are stored under directories based on the date of the post. Consider this hypothetical post, under posts/tech/

  Title: Why Do I Do this?
  name: why-do-i-do-this
  Date: 2017-06-08


The URI for this post would end up being /2017/06/08/why-do-i-do-this.html, even though the input file has a different name.

Other Uses of the name Metadata and Internal Link Types

In addition to providing the base name for the markdown file generated in the docroot, this metadasta has other uses.

First, it is also used as the name of the by-name index directory in the docroot, which contains a link to every post by name, assuming the names are actually unique.

Second, it can be used to refer directly to another post via the page internal link type. The "Link Types" section, below, summarizes all of the special link types available.

Link Types

Static Pages

Static pages can be considered a very simple wiki. Any MultiMarkdown files in the pages subdirectory are processed and added to the overall list of pages, which is turned into in the docroot. This is effectively a navbar for your pages, suitable for inclusion in boilerplate. We pull into every page in the final docroot, in the header. The order metadata in pages is used to sort them, it should be an integer and there is no default, so it will cause warnings if you omit it.

Hierarchical Documents

Books and papers both share a hierarchical structure and are therefore treated more or less the same by Flog. The only differences are cosmetic: books are comprised of named chapters where as papers have numbered sections. The order of chapters and sections is determined by the Order metadata, which should be an integer. The chapters and sections of books and papers are sorted by their Order metadata in the docroot.

As with pages, the order of chapters and sections is determined by the Order metadata, which should be an integer.

In the final docroot each book or paper will display the hierarchical structure of the files in the input.


  0.1.23  19 Mar 2024   attila  add -L links.txt, link:...
  0.1.22  07 Feb 2024   attila  add -a (no_archives)
  0.1.21  12 Dec 2023   attila  merge changes from other laptop
  0.1.20  24 Jul 2023   attila  fix -Y, parselapsed, clean up
  0.1.19  04 Feb 2023   attila  add RSS, draftiness levels
  0.1.18  23 Jan 2023   attila  add content warning functionality
  0.1.17  16 Nov 2022   attila  add -y option
  0.1.16  21 Feb 2022   attila  add post: links and /by-name/
                                OOify and refactor a little
  0.1.15  04 Sep 2017   attila  fix link
  0.1.14  03 Sep 2017   attila  clerical error
  0.1.13  03 Sep 2017   attila  -b => -O, new -b = $bare_pages,
                                fix -C and -N, add hidden pages
  0.1.12  04 Jul 2017   attila  dump_tags
  0.1.11  13 Jun 2017   attila  $ignored_meta_re, page summs, more POD
  0.1.10  11 Jun 2017   attila  funkylinks, flog POD improvements
  0.1.9   27 May 2017   attila  strip text for summaries, fix titles
  0.1.8   24 May 2017   attila  Modern::Perl, $no_{books,papers}
  0.1.7   16 Dec 2016   attila  make pages obey draft attr
  0.1.6   26 Sep 2016   attila  add draft feature via metadata
  0.1.5   10 Aug 2016   attila  bug: featured ^ recent
  0.1.4   06 Aug 2016   attila  Featured posts, timestamp
  0.1.3   04 Aug 2016   attila  Refactored for books + papers
  0.1.2   03 Aug 2016   attila  Books
  0.1.1   01 Aug 2016   attila  First working version
  0.1.0   12 Jul 2016   attila  Started