Follow me on my journey on how this blog post was written and published. You may discover an amazing workflow to quickly bring your thoughts to the web as well.
The basic setup with org-mode
In the beginning there was darkness. Then, a fresh new .org
file appears. The opensourceodyssey.org
file, to be precise.
Org
or org-mode is a markdown syntax for Emacs. You can do a lot with it; such as: taking notes, tracking tasks, writing literal code files (like Jupyter notebooks for Python), and exporting to all kinds of other formats (like .md
, .html
or reveal-js
presentations).
Every post you read is a level 2 heading in the opensourceodyssey.org
file that is marked as “DONE”.
Notice in the top, the #+hugo_base_dir
variable, that has a value of a server ssh alias and a file path on that server. We’ll get back to this.
In summary, org and Emacs is the place to write the content.
Go Hugo!
Next step is to setup a simple hugo project. Hugo calls itself “The world’s fastest framework for building websites” and you can confirm this by navigating this very site. It specializes in static site generation, in other words sites where the information does not change based on the user. This is perfect for a blog.
After installing hugo
, just create a new hugo project with hugo new site [SITENAME] --format yaml
. Replace [SITENAME]
with the name of your project. The entry folder will have this name. I prefer YAML over TOML, so I opt for YAML.
Hugo takes care of the “business logic”. To make things look pretty, we need a theme. Hugo offers a wide variety of free-to-use themes.
I picked a beautiful theme: PaperMod. The features include a light/dark mode, posts with navigation to the previous/next post, automatic word count and reading time calculator and much much more.
Each theme comes with its own installation instructions. Follow them and make the changes in your newly crated hugo
project.
Great! In short, with org
we write the posts and with hugo
we render the HTML for a browser.
Publishing the post with ox-hugo
Now we need some kind of glue to tie it all together. Fortunately, Emacs has us covered with ox-hugo. Moreover, Doom has maybe the easiest installation ever for this integration:
Go into the init.el
of your ~/.config/doom
and change the line of org to be like
(org +hugo)
then rebuild the configuration (with SPC h r r
).
Now, in any org document you can execute the org-export-dispatch
(with SPC m e
) to pop the export menu.
Then I select “Export Hugo-compatible Markdown” and choose to export all subtrees. The destination will be the #+hugo_base_dir:
we set at the top of the file!
Yes, it’s that easy. Navigate to the hugo directory and check the content/
directory for your posts.
Adding metadata to a post
You should also add metadata to the individual subheadings to support the “export all subtrees” command.
The “Posts” h1 has the following properties right underneath it:
:PROPERTIES:
:EXPORT_HUGO_SECTION: posts
:END:
And the individual h2 (that each represent a post) have the following properties underneath it:
:PROPERTIES:
:EXPORT_FILE_NAME: reverting-to-the-default
:EXPORT_HUGO_SECTION: posts
:EXPORT_HUGO_PUBLISHDATE: 2024-07-17
:EXPORT_HUGO_CUSTOM_FRONT_MATTER: :cover '((image . "/ox-hugo/touching-base.png") (alt . "A ship docked at land"))
:END:
Notice the :EXPORT_HUGO_CUSTOM_FRONT_MATTER:
setting. ox-hugo
supports putting any custom frontmatter in the generated .md
document! According to the PaperMod docs and sample Page.md
, the frontmatter for a cover image looks like this:
cover:
image: "<image path/url>" # image path/url
alt: "<alt text>" # alt text
caption: "<text>" # display caption under cover
relative: false # when using page bundles set this to true
hidden: true # only hide on current single page
cover
is the first-level key, containing an object. Ergo: (:EXPORT_HUGO_CUSTOM_FRONT_MATTER: :cover '((image . "/ox-hugo/touching-base.png") (alt . "A ship docked at land"))
). Now this just works when triggering ox-hugo
export.
The tags of each post is set directly behind the heading with :tagname:
syntax. Our hugo theme takes care of creating pages for our tags.
We can then test our setup on localhost with executing hugo server
in the hugo base directory. http://localhost:1313
then shows our site.
Hosting the site
Finally, we have our site and our content and we just need to bring it online.
For this I rented a VPS and a domain. Point the domain to the static IP of the VPS with DNS records. Then all that is needed is a webserver on the VPS to handle requests. I chose caddy
. Caddy is a very easy to use webserver that automatically handles certificates for you.
Since Hugo is a static site generation tool, caddy is simply serving static files. Here is the Caddyfile
config for a hugo blog.
opensource-odyssey.net {
root * /server/public
file_server
}
www.opensource-odyssey.net {
redir https://opensource-odyssey.net{uri}
}
💡 /server/
is the directory where the hugo site lives. It’s owned by the caddy
user and group.
The hugo
command builds our site and places everything in the public/
folder.
Now restart caddy
with sudo systemctl restart caddy
and voila, the site is online!