I’ve wanted to write about how I manage my blog since last year, when I wrote my first blog review1. Quoting directly myself from that blog post:

I won’t go into the technical details of this setup right now, but I can’t stress enough just how good this setup is for someone who already likes Emacs a lot (such as myself). With this setup I’m able to focus exactly on the only thing that matters: writing.

Of course, wanting to write something is a thing, actually getting down to writing it is a completely different thing. Between those two moments, life happens, and everything that it entails. But since life contains everything, it also contains the few, brief and special moments when I sit down to write these blog posts. And now, finally, here we are: in this blog post I will describe in details the environemnt I’ve found for writing and managing my blog.

Enjoy the read!

Part 0 – General Overview

The managing of a blogging platform such as mine makes use of the following components:

  • hugo, a static site generator written in go that takes in input resource files such as markdown files, image files, and other sort of resources and produces as output a complete static site that can then be directly exposed to the internet.

  • emacs, as the main text editor for writing.

  • org-mode, as the markup language used to write and manage the content of my blog within emacs.

  • ox-hugo, an emacs package that is used to export org-mode files (.org) into a hugo-compatible markdown (.md) format.

What this all means, essentially, is that I work within a pipeline of three stages:

  1. I write and manage my blog posts in a single org-mode file using emacs.

  2. Whenever I modify the content of this org-file, ox-hugo works in the background to export the updated content into an appropriate markdown file.

  3. When I’m ready to publish the updated content, I use hugo to generate the new static site, which is then pushed to my server.


\[\text{org-mode} \xrightarrow{\boldsymbol{\text{ox-hugo}}} \text{markdown} \xrightarrow{\boldsymbol{\text{hugo}}} \text{html, css, js}\]

To explain all the pieces of this puzzle, I will start by first explaining what is a static site generator, then I will briefly mention how to start using hugo, and finally I will show how to manage the blog’s content directly in org-mode within emacs by using ox-hugo.

Part 0.5 – Why a static site?

Suppose, for the sake of discussion, that you are interested in writing about movies because you are very passionate about the film industry, you have a lot to say about the various movies you see and, out of this passion, you’re thinking of building an online platform to share your passion with, hopefully, other like-minded people. To fulfil your desire in a meaningful way it is important to first ask yourself the following questions:

  • What kind of end-format do I wish to have for my content?

  • How do I want to publish it?

  • How will I manage it?

  • What kind of shape will take the act of creating new content or modifying the content that already exists?

Since the rise of the Web, one of the simplest and most effective way to share content on the internet has been through the usage of web servers. I’ve already written something about the web in the article related to web scraping, a technique which aims to extract structured information from web pages2. In this blog post I just want to mention the fact that web servers allow anyone to share content online with limited effort.

Now, when talking about web servers and web content, it is crucial to mention that there are different types of web servers each of which is used to service a specific kind of web content. Simplifying matters quite a bit, we find the following two main types of web servers:

  • Those that serve the same content to all the entities that request for it.

  • Those that serve specialized content depending on the entity that requests it.

The first type we call static web servers, because they serve static content, which is content that does not change once it has been exposed as a public resource on the Web. The second type instead we call dynamic web servers, because they serve dynamic content, which is content that can potentially change everytime a new entitiy makes a request for it.

The second type of web servers are clearly more powerful for the simple fact that they can generate specific, individual content depending on the client’s request. Historically, dynamic web servers came after static web servers. Given that we can now choose dynamic web servers, why would anyone still use static web servers?

While the second type of web servers might seem better because they are more powerful, it is important to be aware that, as someone’s uncle wants to remind us,

Indeed, this extra power comes from the fact that dynamic web servers internally make use of specific interpreters that execute code for every request made by the clients. An example of such an interpreter is php. The php interpreter gets the client’s request data from the web server, and uses such data to generate the response, that is then sent by the web server back to the client.

\[\text{browser} \longleftrightarrow \text{web server} \longleftrightarrow \text{php interpreter}\]

Given that dynamic web servers introduce components which, by construction, execute code on the server depending on the client’s request, it is no wonder that there can be significant security concerns regarding the implementation of such tecnology, especially if the code of the website has not been written with security in mind, which, sadly, is too often the case. Wordpress is the typical example of a blogging platform that makes use of php in order to generate dynamic content.

Thus, to avoid any sort of code execution, the simplest thing to do is to skip altogether this dynamic generation of content and to stick with old school static sites that only serve static assets such as html, css and js files. By not having any extra code being executed on our servers, we are reducing the attack surface area. While this makes sense, we might still be wondering:

  • Will this return to static sites bring unwanted consequences?

  • Are we forced to write directly html code if we want a static site?

It could be cumbersome to write directly in html, as it is moderately verbose and we might want a better, more clean alternative. To lessen the pain, blogging platform such as wordpress implement internal text editors using the power of dynamic execution. What happens then if we lose such power?

Static site generators are built to solve this problem. They are tools that allow one to manage the creation and subsequent modification of static content. They can be seen as pipelines that take in various form of resources and, at the end, produce a sort of “public folder” which contains the static site generated and which can be put within a web server public’s folder to expose the generated site to the internet for all to see.

Of course, as in most things in computer science and technology, there are many different static site generators, and hugo is only a single implementation of such idea. I’m not sure how it could be compared with others like-minded tools, as it is the first ever static site generator that I use, but so far I’ve found no deal breakers with it.

So, let’s continue with some details about making hugo work.

Part 1 – A brief introduction to Hugo

Hugo is an open-source effort aimed at building an efficient and powerful static site generator. The first thing to do if we’re interested in learning about hugo is to download and install it. To this end, let us assume to be working on the following ubuntu version

  lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04 LTS
Release:	20.04
Codename:	focal

To install hugo we can use the package manager apt as follows

  sudo apt update

  sudo apt install hugo
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  git git-man liberror-perl libsass1
Suggested packages:
  git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui gitk gitweb git-cvs git-mediawiki git-svn
The following NEW packages will be installed:
  git git-man hugo liberror-perl libsass1
0 upgraded, 5 newly installed, 0 to remove and 555 not upgraded.
Need to get 16.0 MB of archives.
After this operation, 86.0 MB of additional disk space will be used.
  Do you want to continue? [Y/n] Y

After having installed it, we can check if the binary was installed correctly by printing out the version

  which hugo
  hugo version
  Hugo Static Site Generator v0.68.3/extended linux/amd64 BuildDate: 2020-03-25T06:15:45Z

To create a new site we can use the command hugo new site <folder>. This command will create a new folder within the directory we’re currently in. Say that we are in /home/leo/Desktop/, then we execute

  hugo new site movie-reviews
Congratulations! Your new Hugo site is created in /home/leo/Desktop/movie-reviews.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme THEMENAME" command.
2. Perhaps you want to add some content. You can add single files with "hugo new SECTIONNAME/FILENAME.FORMAT".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

this creates a new folder, which in our case we have named movie-reviews since we that is the argument we used when calling the command.


By going in that folder we can see the default structure of a hugo project

   cd movie-reviews
   tree .
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes

6 directories, 2 files

the most important file is the config.toml, which contains the basic configuration for our site

baseURL = "http://example.org/"
languageCode = "en-us"
title = "My New Hugo Site"

Let’s change the title to a more appropriate one, say “Moview Reviews”

baseURL = "http://example.org/"
languageCode = "en-us"
title = "Movie Reviews"

If we now want to see how our site would look like when exported and exposed on the internet, we can use the command hugo server -D. This command starts a local web server which serves the content of the site generated by hugo.

  hugo server -D
Building sites … WARN 2022/09/01 18:03:01 found no layout file for "HTML" for kind "home": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.
WARN 2022/09/01 18:03:01 found no layout file for "HTML" for kind "taxonomyTerm": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.
WARN 2022/09/01 18:03:01 found no layout file for "HTML" for kind "taxonomyTerm": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

		   | EN
  Pages            |  3
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  0
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Built in 6 ms
Watching for changes in /home/leo/Desktop/movie-reviews/{archetypes,content,data,layouts,static}
Watching for config changes in /home/leo/Desktop/movie-reviews/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address
Press Ctrl+C to stop

To actually see the website we have to visit the local url http://localhost:1313/ using any browser.

Surprise surprise, right now it is a bit too much empty!

To start seeing something beautiful without too much effort the idea is to use pre-defined themes, which can be explored from hugo’s official site in the themes section: https://themes.gohugo.io/.

The one I personally use is the hugo-theme-terminal.

reading the documentation we can see that in order to use it we have to clone the repository of the theme within the themes folder of the hugo site as well as update the config.toml file with the one they have in the documentation. Here I will showcase a simple configuration for our blog about movie reviews

baseurl = "/"
languageCode = "en-us"
theme = "terminal"
paginate = 5

  # dir name of your main content (default is `content/posts`).
  # the list of set content will show up on your index page (baseurl).
  contentTypeName = "posts"

  # ["orange", "blue", "red", "green", "pink"]
  themeColor = "orange"

  # if you set this to 0, only submenu trigger will be visible
  showMenuItems = 2

  # show selector to switch language
  showLanguageSelector = false

  # set theme to full screen width
  fullWidthTheme = false

  # center theme with default width
  centerTheme = true

  # if your resource directory contains an image called `cover.(jpg|png|webp)`,
  # then the file will be used as a cover automatically.
  # With this option you don't have to put the `cover` param in a front-matter.
  autoCover = true

  # set post to show the last updated
  # If you use git, you can set `enableGitInfo` to `true` and then post will automatically get the last updated
  showLastUpdated = false

    languageName = "English"
    title = "Movie Review"
    owner = "Leonardo Tamiano"
    keywords = "movies reviews"
    copyright = ""
    menuMore = "Show more"
    readMore = "Read more"
    readOtherPosts = "Read other posts"
    newerPosts = "Newer posts"
    olderPosts = "Older posts"
    missingContentMessage = "Page not found..."
    missingBackButtonLabel = "Back to home page"

      logoText = "Menu"
      logoHomeLink = "/"

        identifier = "about"
        name = "About"
        url = "/about"

Now if we execute again hugo server -D we get the following result

Let’s now write our about page. This can be done by going in the ./content folder and writin an about.md file with the following content

title = "About"
lastmod = 2022-09-02T03:24:10+02:00
draft = false

Hello, stranger on the internet, this page is all about me!

My name is **Leonardo Tamiano**, and I am really passionate about movies and all things related to cinema. I want to use this platform as a means to share this passion with like-minded people.

I hope you enjoy my writings.

What's your favorite movie?

Notice how at the top of the file we find a series of attributes such as title, lastmod and draft. These are properties of the file which are accessible from hugo’s internal engine during exporting. After these attributes we find the content itself, which is written according to blackfriday, a specific implementation of markdown in go (remember that hugo is also written in go).

By saving this file as ./content/about.md we can directly see the new exported content in the browser, because when using the hugo server -D command any change we make to the content of the blog is directly exported and refreshed in the background.

Now, it’s to mention that when using external themes, the specific way in which the site is generated heavily depends on the particular implementation of the theme we used. This is because hugo is a framework for generating static sites, and its interface can be used differently by different themes.

In our case to make things work what matters is that in the config.toml file we have specified as the url the value url = "/about" for the about entry in the menu and that the file of the about page was called either about.md or About.md. If, for example, we had called the about page file something like test.md, then things would not have worked anymore.

This is all to say that the abstraction at which we are working here is not trivial, and one must discover all these little ways in which hugo interacts with the specific themes we’ve choosen and how we can obtain the result we want. Said in another way, it is necessary to spend some time tinkering away in order to understand how this generation process works.

Before discussing how to integrate org-mode in all of this, let’s suppose we want to write our first movie review. The idea is to create a new .md file within the ./content/posts directory, as the contentTypeName property in the config.toml has the value "posts".

# ...

  # dir name of your main content (default is `content/posts`).
  # the list of set content will show up on your index page (baseurl).
  contentTypeName = "posts"

# ...

Given that recently I have re-watched with my brother “2001: A Space Odyssey”, the well known film by Stanley Kubrick, let’s write a brief review about it in the file ./content/posts/2001_a_space_odyssey.md

title = "2001: A Space Odyssey"
lastmod = 2022-09-02T03:24:10+02:00
draft = false

In "2001: A Space Odyssey", Kubrick does not merely
tells a sci-fi story of space exploration,
starhips, and other things of the sort.
In this film, Kubrick talks about the
entire history of humans, from our ancient origins,
to a most fascinating possible future.


Having saved it we can view it exported in the home page section. If we click on the title we also get the single page view of the post.

Once we are satisfied with our writings, we can export all the assets within a single folder by simply calling hugo with no extra parameter.

archetypes  config.toml  content  data  layouts  resources  static  themes
		   | EN
  Pages            | 11
  Paginator pages  |  0
  Non-page files   |  0
  Static files     | 15
  Processed images |  0
  Aliases          |  2
  Sitemaps         |  1
  Cleaned          |  0

Total in 68 ms
archetypes  config.toml  content  data  layouts  public  resources  static  themes

Observe how the public folder that has been created represents our final static site.

root@ubuntu:/home/leo/Desktop/movie-reviews/public# tree -L 1
├── 404.html
├── about
├── assets
├── categories
├── img
├── index.html
├── index.xml
├── page
├── posts
├── sitemap.xml
└── tags

7 directories, 4 files

We can then take this folder, move it under the root directory of a web server, and expose it to the internet. Of course, to actually share this to the internet we need to handle a bunch of other things, such as the issue of hosting, which is too complex to discuss it in here.

For the sake of example, suppose we want to test out our static site using a local python web server. We can copy the public folder somewhere else and start a local python web server within it.

  cp -r public/ /home/leo/cd /home/leo/publicpython3 -m http.server 1337
Serving HTTP on port 1337 ( ...

When we go to with our browser we get the same view as before. This means that if we copy this directory anyhwere else, we get back the same content.

Part 2 – Write directly in org-mode with ox-hugo

Now that we are able to write posts using markdown and export those posts into html and other assets using hugo, we can start to understand how to integrate the final pieces, emacs, org-mode and ox-hugo in all of this.

First of all, why would anyone want to add emacs to the mix? As always, with emacs it is a matter of integration. To someone who’s used with emacs, it is simply a joy to work within emacs. Especially considering the power of org-mode when dealing with textual content, the idea of being able to manage within org-mode an entire blog made up of various blog posts is a pretty cool one, at least to me.

To make all of this work, we first have to install emacs as well as the ox-hugo package. To bootstrap the process of initializing emacs, the following small configuration should suffice. It has to be saved in the home directory of the user under the name .emacs. It is also necessary to have cloned the package use-package on the file system, also in the home directory of the user.

;; Added by Package.el.  This must come before configurations of
;; installed packages.  Don't delete this line.  If you don't want it,
;; just comment it out by adding a semicolon to the start of the line.
;; You may delete these explanatory comments.

;; NOTE: execute 'git pull https://github.com/jwiegley/use-package'
;; from within the home directory of the user
(add-to-list 'load-path "~/use-package")
(require 'use-package)

(use-package package
  ;; Set priorities of using melpa-stable.
  ;; The higher the number, the higher the priority.
  (setq package-archive-priorities
	'(("melpa-stable" . 2)
	  ("MELPA" . 1)
	  ("gnu" . 0)))

  ;; Add to list melpa stable
  (setq package-archives
	`(("melpa-stable" . "https://stable.melpa.org/packages/")
	  ("MELPA" . "https://melpa.org/packages/")
	  ("gnu" . "https://elpa.gnu.org/packages/")

(use-package ox-hugo
  :ensure t
  :after ox

Once we have all of this, we can create a new file named content.org within the movie-reviews folder previously created by hugo. In it, we can put the content of the only post we had previously written.

Notice how the information is structured in the org-file. The most important thing, in order, is the presence of the STARTUP and HUGO_BASE_DIR variables at the top of the file, then we find the org entry blog posts which contains the property EXPORT_HUGO_SECTION, which we gave it the value “posts” because in our hugo projet the various posts are written to the ./content/posts folder (remember the value of the contentTypeName property in config.toml?), and then for each outline within this section we write the EXPORT_FILE_NAME to represent that the outline contains a new hugo post. The value of the property represents the final name of the .md post which should later be processed by ox-hugo.

After having written it, we have to execute M-x org-hugo-auto-export-mode ENTER and then everytime we save a post it will automatically be exported as an .md file in the filesystem. If we combine this with the previously shown hugo server -D in the background, we can watch our changes live.

The power of this setup is that now we can posts easily by just writing another org outline with a different content.

Now, having a single org file for all the content is my choice. Other people might prefer having different org files for different entries. The idea however is that you can manager this however you want. What ox-hugo does, essentialy, is it provides an emacs-friendly interface for generating the markdown content required by hugo, since what’s important for hugo to work properly is the presence of the markdown files.

Having discussed the various components, as well as how they connect with eachothers, we can conclude with some final words.

Part 3 – Conclusion

First of all, I hope this explanation was clear to everyone, but if there still some doubt about how it all works, a comment down here is welcomed, or also an email. I’ll try to explain better.

Then, some more general thoughts. Sometimes I wonder if such setup is simply too complex, as it requires a lot of different components. I feel that the issue of complexity in software is often not given enough space. I mean, yes, everything talks about complexity, but the systems we use are becoming ever-increasing complex, as they require more and more components and as the interation between those components become ever more sophisticated.

Right now what matter to me is that this setup has worked for the last \(2\) years surprisingly well, requiring little to no maintenance whatsoever. As I have briefly mentioned at the start of the blog post, with this setup I’m able to focus entirely on the only thing that actually matters: the writing. Thus, for now, this complexity seems to be worth the price, at least for me.

I hope reading this has somehow helped you!