This is a technical post on my experience so far for blogging with ox-hugo and publishing it on sourcehut pages. Both have very good getting-started guides (just click the links), so here I’ll focus on my experience and the technical specifics of using both in combination. You can check out the source at


I use the org-mode of the emacs as my daily engine for note-taking (via org-roam), journaling and for planning my agenda. Therefore, it appealed to my to write a blog in it to reduce context-switching and facilitate making blog entries from existing notes.

I found ox-hugo to be a nice solution to my needs. It is an emacs package that exports an org-mode file to multiple markdown files that are then used by the hugo static site generator to generator the website. This is not the simplest approach, there are emacs packages to generating static sites directly without the need for intermediate software (see org-blog-wiki), alternatively I could have used hugo directly which also has rudimentary markdown support. But this seems to works for me, it’s nice being able to leverage the full power of org-mode and also profit from the large hugo ecosystem and user-base (see why ox-hugo). And I don’t have to fear lock-in, since I have control over the org- and markdown-files on my computer and might switch over to any other system if I ever decide to. This is what I love about having a static site.

Figure 1: Writing this blog post in org-mode

Figure 1: Writing this blog post in org-mode

Publishing to sourcehut

I recently discovered sourcehut as a git-forge and thus lightweight and privacy-respecting alternative to github, gitlab and the likes. Since recently it also allows hosting static websites via sourcehut pages, so I decided to give it a try. What I liked from the start is that after getting an access token it allows deploying your website via curl with a simple command (see, no other configuration needed. This helped getting started quickly without any further configuration needed and seeing my blog online was a definite motivation boost. All I needed was a simple shell command for building the hugo website (export to hugo happened from emacs) and deploying it, which I saved in a shell script:

hugo &&\
    tar -C public -cvz . > site.tar.gz && \
    curl --oauth2-bearer "$( pass access-tokens/  )" \
# cleanup
rm -v site.tar.gz

As you see, I insert access token via the pass password manager, so I don’t have to reveal it in the repository. I could extend on this script to fully automate the deployment, e.g. with git commit hooks etc. But I decided to try to automate the deployment with via a sourcehut build manifest. Since my set-up is a bit obscure, having a build-manifest ensures a level of reproducibility, e.g. ensuring that others will be able to fork the blog repository and build it without having to set up emacs and running interactive commands from within it and also without having to commit the generated markdown files. In addition, I just wanted to gain experience with build-systems.

What I was missing was an emacs lisp script that installs ox-hugo on the virtual machine of the build-system and then runs it to export the blog. After reading some blog/stackoverflow posts emacs scripting, I ended up with ox-hugo-export-blog-script.el, which can be run on a fresh system with

(require 'package)

;; List the packages you want
(setq package-list '(ox-hugo))

(add-to-list 'package-archives '("melpa" . "") t)
;; Activate all the packages (in particular autoloads)
;; Update your local package index
(unless package-archive-contents

;; Install all missing packages
(dolist (package package-list)
  (unless (package-installed-p package)
    (package-install package)))

;; disable backup file generation, don't need this in script mode
(setq auto-save-default nil)
(setq make-backup-files nil)
;; allow all local variables, since interactive prompts are problematic in script mode
(setq enable-local-variables :all)

;; open blog and export it to markdown iwth ox-hugo
(find-file-existing "../content-org/")
(org-hugo-export-wim-to-md 'all-subtrees)

This allowed me to build the website with the following .build.yaml:

image: alpine/latest
  - emacs
  - hugo
  - ox-hugo-md-export: |
      cd ~/blog/build
      emacs --quick --script ox-hugo-export-blog-script.el      
  - hugo-generate-site: |
      cd ~/blog
  - package: |
      cd ~/blog
      tar -C public -cvz . > site.tar.gz      
  - upload: |
      cd ~/blog
      acurl -f -Fcontent=@site.tar.gz      

This builds take about 37 seconds which is quite a bit longer compared to a simple hugo build due to the required installation of emacs and ox-hugo, but still acceptable. In my experience so far the build system sometimes still has upstream problems and might be a while in pending, so I still keep my curl-deploy script in case I’m impatient with deploying

Update: Alternative approach by System Crafters

Shortly after I posted this, the System Crafters posted his own livestream on publishing an org blog on sourcehut, but he uses ox-publish, so make sure to check that out:

System Crafters Live! - Static Websites with Emacs, Org Mode, and Sourcehut • Episode planning • Q&A