Minimal Blogging Setup with Orgmode and ssg5

Published 2020-07-21 on Anjan's Homepage

My Old Setup

I prefer orgmode over markdown because already I use orgmode for appointments, reminders, notes, etc. However, most static website generators only support Markdown. Luckily, orgmode has an html export function: M-x org-publish. org-publish is much more powerful and reliable than any markdown converter I have used.

Since 2016, I have been using jekyll which is bloated and requires a lot of ruby dependencies. To make sure my website always built, I setup sourcehut builds to build my jekyll website. Each website build takes 3 minutes on sourcehut.

Building the website locally was also a terrible experience. I had hacked on a shell script that would take my org-publish output and make it usable for jekyll by setting the title and layout variables. To publish a new blog post, I had to: org-publish -> run my script -> commit html AND org file -> wait for jekyll to build on sourcehut. If I had made a mistake, I would repeat the entire process… Eventually this setup was so cumbersome, I stopped blogging.

My New Setup

I do not use most of the features of jekyll and needed something more minimal. In the past, I have dabbled with werc, hugo, and guile-haunt. Eventually, I stumbled upon ssg5 which is a static site generator written in 180 lines of posix compliant shell. If you do not use markdown, ssg5 will only use unix coreutils to make your website with a common header and footer. Perfect!

I decided I should go through the pain of porting my website over to ssg5 and documenting my experience. Here is what I had to pay attention to:


To generate the “Articles from blogs I follow around the net” at the bottom of each blog post, I use openring. Openring is a great way to support a more decentralized internet where everyone has their own website. A template for the blog post footer is saved as webring-in.html. My script downloads openring and prints to stdout the html to be included at the end of blog posts.

Changes to rssg

To generate my rss feed, I use rssg. However, rssg requires a list of posts on the homepage. Unfortunately, ssg5 does not generate a list of posts and I had to hack together Here is the output from

<li>2020-07-21 <a href="/posts/2020-07-21-ssg.html" title="2020-07-21">Minimal Blogging Setup with Orgmode and ssg5</a></li>
<li>2020-07-06 <a href="/posts/2020-07-06-crust.html" title="2020-07-06">Compiling crust for pinephone on Postmarketos</a></li>
<li>2016-04-26 <a href="/posts/2016-04-26-Hello-World.html" title="2016-04-26">Hello World</a></li>

My fork of rssg is located here. Gnu coreutils’ date command does not have a -j flag as required by the date_rfc_822() function in rssg. The dates output by are in YYYY-MM-DD format. To convert to rfc_882, I used the following date command in the date_rfc_822 function:

date_rfc_822() {
      date -R -d "$1"

Changes to ssg5

My fork of ssg5 is located here. I modified ssg5 to run openring, run rssg, and change the footer depending on the page. Running openring takes alot of time which is inconvenient when editing a page locally so I implemented a -f flag to ssg5. If -f is the fifth argument into ssg5, the script doesn’t generate the webring.

In the main function on line 52, I load in the “Articles from blogs I follow” html into an environmental variable called $WEBRING:

[ "$5" != "-f" ] && WEBRING="$(./" && export WEBRING

On line 53, I generate the blogfeed to be included at the bottom of the homepage:

FEED="$(./" && export FEED

Depending on whether the page is homepage or a blog post, I need to include a different footer. If the page is a blog post, I must include the openring output and if the page is the homepage I must include the blog post log (generated by As such, I changed the render_html_files() function to:

render_html_files() {
    while read -r f
     echo "$1/$f" | grep "index.html" > /dev/null && EXTRAFOOT="$FEED" && export EXTRAFOOT
     echo "$1/$f" | grep "posts" > /dev/null && EXTRAFOOT="$WEBRING" && export EXTRAFOOT
     render_html_file "$3" < "$1/$f" > "$2/$f"
     EXTRAFOOT="" && export EXTRAFOOT

If the file name matches the regular expression index.html, it gets $FEED as the extra footer. If the file name is matches the posts regular expression, $WEBRING is the extra footer.

Finally, I modified the render_html_file function to include my environmental variable $EXTRAFOOT before $FOOTER:

        print body
        print ENVIRON["EXTRAFOOT"]
        print ENVIRON["FOOTER"]

At the end of the main function in my ssg script, I use rssg to generate my site’s rss feed:

[ "$5" != "-f" ] && ./rssg dst/index.html 'Anjan Momi Homepage' > dst/feed.xml


In my .spacemacs, I added the following lines:

 (setq org-publish-project-alist
          ;; Path to org files.
          :base-directory "~/code/"
          :base-extension "org"

          ;; Path to ssg osts
          :publishing-directory "~/code/"
          :recursive t
          :publishing-function org-html-publish-to-html
          :body-only t ;ssg will not add header and footer if an <HTML> tag exists
          :link-up /index.html
          :html-postamble t
          :toc nil

Open an org file from your website and run C-c C-e P p to generate the html for your blog files.

If you edit the org-publish-project-alist variable and want to regenerate all files (even unmodified) run: C-u 1 M-x org-publish.


To simplify the deployment of my website, I added the following aliases to my zshrc:

alias makesite="rm dst/.files; ./ssg5 src dst \"Anjandev\'s Homepage\""
alias deploysite="rsync -rvPz --delete dst/ deploy@homeserver:/"

In the makesite alias, rm dst/.files ensures ssg5 will rebuild all the website’s pages.


To check mistakes with my website’s rss and html, I used w3c’s validation service for html and rss.

Finally, I had to go back in time and convert all my Markdown posts to orgmode.

My website now generates in under a second for a clean build compared to 3 minutes with jekyll. I don’t have to use jekyll, jekyll’s dependencies, and sourcehut to build my website. Expect more blog posts going into the future!

Have a comment on one of my posts? Start a discussion in my public inbox by sending an email to ~anjan/ [mailing list etiquette]

Articles from blogs I follow around the net

These articles/blogs do not represent my own opinions or views.

Todo.txt-more: Efficiently managing your todo list and your time

Todo.txt More: Efficiently managing your todo list and your time Introduction I tend to get fairly enthusiastic when it comes to lists, even more so when there's a chance to optimise my productivity. The end of the year is a time for looking backward an…

via Proycon's website December 31, 2022

The PineTab2 is a new, faster Linux tablet - and it's not alone

In their December update, Pine64 announced the PineTab2, which is the successor to their PineTab from 2018. As a major change, the PineTab2 upgrades the slow A53-based A64 SoC with an A55-based Rockchip RK3566, the same chip that was used for the Quartz64…

via TuxPhones - Linux phones, tablets and portable devices December 19, 2022

I shall toil at a reduced volume

Over the last nine years I have written 300,000 words for this blog on the topics which are important to me. I am not certain that I have much left to say. I can keep revisiting these topics for years, each time adding a couple more years of wisdom and impro…

via Drew DeVault's blog December 1, 2022

Generated by openring