Minimal Blogging Setup with Orgmode and ssg5

Published 2020-07-21 on Anjan's Homepage

1 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.

2 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:

2.1 Openring

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.

2.2 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"

2.3 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["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

2.4 Orgmode

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.

2.5 Deployment

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.

3 Conclusion

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.

Linux development is distributed - profoundly so

The standard introduction to git starts with an explanation of what it means to use a “distributed” version control system. It’s pointed out that every developer has a complete local copy of the repository and can work independently and offline, often contra…

via Blogs on Drew DeVault's blog September 2, 2020

Eshell versus M-x shell

I’ve used and defended Eshell for years. Sadly, Eshell has some long standing issues that I grew tired of in the long run. So I’ve decided to switch to M-x shell and see how much of my Eshell workflow I could port. Language and the underlying shell pr…

via Pierre Neidhardt's homepage June 26, 2020

Thermoelectric Stoves: Ditch the Solar Panels?

Wood stoves equipped with thermoelectric generators can produce electricity that is more sustainable, more reliable, and less costly than power from solar PV panels.

via LOW←TECH MAGAZINE May 26, 2020

Generated by openring