Blogging with quarto
1 Blogging platform update.
WordPress blogging works well. There’s one caveat to my personal portfolio website. My production pipeline is slow and cumbersome. I use Obsidians vault system with one zettelkasten for all my writing. This has the benefit of having all data in text form and does not suffer from non-accessible formats in the distant future. From Obsidian I copy the text to WordPress, where I need to format it properly and finally copy it back. This duplication means I need to keep track of two databases.
In addition, the WordPress site requires maintennace. Albeit low effort, it is a mental effort; one other thing to keep in mind.
As a software engineer, I found this redundant source of truth unsatisfactory.
2 Enter Quarto
Then I discovered quarto. Quite relevant in the data science field, it has gained a following in recent years. Jekyll is quite similar, but somehow the whole quarto experience seems more feature-rich. It effortlessly transforms repositories into websites or books.
Another cool feature is live content. Add the raw code to the text, which will lead to creating figures. I did this during my PhD with tikz
for my figures. With quarto, it is not just figures with new formatting, it can run whole scripts to generate the entire data.
This becomes really handy when running Jupyter notebooks or small python programs habits, which have a small runtime, but a high development effort.
2.1 The ideal blogging pipeline
- Do some work
- Write about it
- Create pleasant images
- Format it properly
- Publish
Quarto can combine steps 2 to 4 in one. It can even integrate the publishing process.
The main issue is not with quarto, but integrating quarto in an existing working process.
If you have done something for a while, you have developed habits. In a professional setting, people usually formalize those habits as processes. Introducing something new is usually challenging. It is hard to change your habits, and it is also hard to change long running processes.
For me, the conflict arose from using Obsidian, a zettelkasten note taking system. In addition, I was used to a WordPress based workflow.
But let’s dive into the details.
3 Setting up Quarto
There are some cool tutorials. As a software engineer, I found the Quarto documentation very well structured. Quarto gallery has a few inspirations on how to format the blog.
Importantly, these sources address the following more complex topics:
- newsletter
- comments
- legal notice
- google analytics
So far, I have nothing to add to those fabulous resources.
4 Switching to Quarto
4.1 Content Structure
I put the old content in the new blog as a new platform is usually best judged with some content in it.
There are two things needed: a better directory structure and yaml front matters.
I have been using Obsidian so far with a flat file structure. However, Obsidian has no issue with all nested structure, as long as the blog remains in the Obsidian vault.
As the blog content should still be accessible from one Obsidian vault, I put the Blog in the Zettelkasten.
Zettelkasten/
├── blog/
│ ├── posts/
│ │ ├── book reviews/
│ │ └── projects/
│ └── normal blog posts
└── normal zettelkasten files
Doing so enables me to link from Zettel outside of the blog to the blog. The otherway round is not intended as Readers would experience dead links.
Quarto advises to use directories to structure the content. And for a good reason. In Quarto, it is easy to generate an auto-created overview, so-called listings, for a directory. I use listings on my book reviews
and projects
overview pages.
Adding the front matters was easy. Filing them with consistent content was more difficult.
The yaml front matter is necessary as it contains all the metadata. This data was previously in WordPress. I prefer this design as I now can change everything from within Obsidian. This mainly comprised creating yaml front matters for all my files.
For new Files, I have a script that adds a template.
4.2 Using drafts
Draft versions of blog post can be quite stable. Sometimes there is no time to finish an article, then again you want some time for the thoughts to age. The article remains in draft
state.
In my WordPress workflow, I had two release gates. The first check occurs when copying from Obsidian to WordPress. I perform the second check before clicking “publish” on the WordPress interface.
Quarto supports this draft state via the yaml front matter draft: true
. You can render or hide drafts. The Quarto YAML defines this via.
draft_mode: visible
For the production version of my blog, I wanted no drafts. However, I found no way to change the behaviour quickly. Therefore, I decided on a custom deployment process via python script.
Usually quarto only informs you that a post is a draft. There is no option to see all your drafts in a rendered version. I added a category draft, which gets set once the draft tag is true.
Again everything is done via python script.
4.3 Output name
Word press automatically shortens the title to something easily readable in the browser address page.
My zettelkasten files usually have the form: 202304170008 Decision paralysis at a McDonalds
. The date shows when I created the note, not when I published the post.
The publishing date and the title is in the yaml front matter. I only need a easily readable webpage without spaces.
I wrote a converter which makes this to decision-paralysis-at-a-mcdonalds
for the output file.
4.4 Dynamic Content
As stated before, I mainly use Obsidian. There are many discussions to get Quarto and Obsidian to work with each other, . However, none of the presented ways worked for me.
One enormous challenge is one of the USP of Quarto is the executing of code from the text files. To make this work, files needed to be qmd
files.
There is an Obsidian extension: qmd as md
. This plugin can currently just show the content of the file and allows editing via Obsidian. Obsidian search or graph do not work.
I resolved the issue by naming my qmd
files qmd.md
. My integration pipeline then changes this to qmd
for the rendering to work, as Quarto can only execute code in qmd
not md
files.
4.5 Wiki links
One of the biggest issues is the linking. Obsidian uses wikilinks, Quarto uses Markdown links.
I tried a lot of things: prerender script, lua filter. These approach failed because it violated the designs of Quarto: a to be rendered file can not be changed and the list of to be rendered files can not be modified by the prerender script.
I settled on the wikilink to markdown Obsidian extension. It only lets you change one link. Changing Back does not correctly work. However, the backlinks and graph view show the markdown links correctly. The only tiring aspect is that the markdown link has %20
for spaces.
There is also the plugin: better markdown links. It offers more features, but did not work with my version.
The community seems onto this one. So I expect better results next year.
5 Publishing with Quarto
5.1 Draft & Publish Directories
So far I have been using Hostinger with a WordPress installation. Getting my work to the blog always required manual copies.
The main reason to switch to Quarto was that it streamlines this quite dump part of the entire writing process. In contrast, Quarto offers support for direct publishing from the repository to a GitHub Pages enabled repository.
What sounds great at first sight, proofed to be rather difficult to get to work.
First, I had the issues with the draft tag and the qmd files. I resolved this issue by introduction extra directories for draft and publishing. A script then runs through all the files and checks and syncs the files. To avoid any privacy leaks, theses directories are outside of my Zettelkasten. Drawback, rendering on render preview is not instant, but requires a manual copy. Future feature: a watchdog process could resolve this.
I then review the publish directory and push it to git, where I have setup github actions.
The complete pipeline is:
- Write in Obsidian
- Use a command to copy to draft or publish directory
- In draft, do a render preview. Any subsequent copy just updates the file.
- Do it with the publishing repo
- Examine new content in git
- Commit and push
- Github renders the repo
- Github updates the blog
5.2 Large Blogs
I noticed that quarto takes some time to render large blogs. Sometimes, there are errors and I needed to delete the render. Which could mean rendering takes a lot of time. Luckily, quarto offers GitHub actions to render your blog on the GitHub server. This has two advantages:
- Not blocking your system with the final render
- Easier diff, as only md source needs review
5.3 Privacy Concerns & Data Leaks
GitHub only allows GitHub pages for public repositories. If you do not feel comfortable to push the raw data of the blog to a public repository, there is a solution.
You need two repositories. One to store the blog source files and the second to store the rendered files with static js.
Here are the instructions to do this in a general case.
To sum up the benefits:
- Separation of Concerns: Keep source files private while hosting the static site publicly.
- Security: Sensitive files in the source repo remain private.
- Version Control: Build files are pushed only to the public repo without cluttering the source repo’s history.
For quarto a different GitHub actions file is required:
name: Quarto Publish
on:
push:
branches:
- master
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source repository
uses: actions/checkout@v3
- name: Setup Quarto
uses: quarto-dev/quarto-actions/setup@v2
- name: Configure SSH for deploy
run: |
# the deploykey is the private key. The public key is in the github pages repo
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Render
run: |
quarto render
# Git task
- name: publish
run: |
# remove git history and settings - to avoid inheritance
rm -rf .git
# re-initialise git
git init
git config user.name "name"
git config user.email "mail@mail.com"
git branch -m main
git remote add origin git@github.com:$USERNAME/$USERNAME.github.io.git
# add and commit /docs/ folder
git add docs
git commit -m "deploy the bundle"
# push the commit to the public repository git push -u origin main --force
6 Code
All code can be found in https://github.com/dolind/obsidian2quarto.