Search

Search IconIcon to open search

Public Second Brain with Quartz

Last updated by Simon Späti

This is my documentation of how I publish my notes from a private Obsidian vault to my Public Second Brain with open-source Quartz and GoHugo.

Video Format

Part of how I publish is also on my YouTube video My Obsidian Note-Taking Workflow about my Note-Taking Workflow article.

# Journey of Publishing Notes

I had several iterations. I used to have an HTML/CSS/PHP website, moved to a traditional blog with WordPress, and now have a Static Site Generators (SSG)-blog with GoHugo and a Second Brain (where you read this note) with GoHugo/Quartz.

On this page, I elaborate on how I can simply write in my Obsidian Vault, add #publish anywhere in my note, and run make deploy and it’s published on my public brain. No conversion, no extra formatting, no nothing needed, just running a make file.

I can write offline, on my mobile phone, or wherever I want; Obsidian will sync it across. Publishing will happen on my laptop (running the make deploy, which essentially does a rsync to my web hosting).

# Publish

How do I publish? There are two steps.

  1. I added a Python script find-publish-notes.py to the Makefile to copy all my SecondBrain notes from my Obsidian Vault with the hashtag #publish and copy it into the Quartz git-repo.
    1. Since 2023-06-07: I switched to a Rust-script that does the same but adds #hashtags properly as Hugo requests, and it is 30x faster. To get the Rust executable, you need to run cargo build --release from within the utils/obsidian-quartz folder.
  2. I ran the make deploy that syncs the changes to my website. This command copies notes with the hashtag “publish” and deploys to brain.ssp.sh. See also more in below .
    1. Since I use GitHub, I could also create a GitHub Action to deploy on commit. But I actually prefer to run the make file.
    2. This make deploy also included a go-program that collects all backlinks into two JSON files assets/indices/contentIndex.json and /assets/indices/linkIndex.json. This small util used is hugo-obsidian (see Makefile) written by Jacky Zhao. Here’s the source. But it is not maintained anymore (as there is now a v4 without it) and it had bugs and didn’t show all my backlinks. That’s why I forked it and fixed the backlinks. You can find it here: sspaeti/hugo-obsidian.

Quartz v3 Workaround

I copy all my Zettels in the root folder instead of adding sub-folders in Quartz. This way I can use Wikilinks [[]] and do not need to change to absolute paths - which I do not want to activate in my Second Brain.

# Deployment

Here I describe how I deploy my second brain.

# Self Hosting

I self-host on my own server as I already have hosting for my website. The only thing I do I rsync the public-folder to my web hosting, that’s all. The script shows how I do it. The actual line is rsync -avz --delete public/ USER@DOMAIN.com:~/www/brain (just replace with your user and domain).

# GitHub

If you do want to use GitHub actions, you can check the Data Glossary (glossary.airbyte.com) which is the same setup as Quartz, but there we use GitHub. Checkout the required deploy.yaml that makes the magic work

Basically, when someone changes the branch hugo, it will deploy changes to master branch and publish that. I think I wrote some more details here.

# Added Features

# Adding an Image (Front Matter)

I added the option to overwrite the header image, and its width and height for each note, as well as the description. E.g. in the Frontmatter I can now overwrite the terms with:

1
2
3
4
5
6
---
ogimage: de-vault.jpg
ogwidth: 1024
ogheight: 761
ogdescription: this is my new description
---

# Obsidian Integration

This Hugo site includes custom processing for two key Obsidian markdown features, allowing seamless migration of content from Obsidian to the blog.

Converts Obsidian-style wikilinks to external links pointing to your second brain.

Source: /layouts/partials/process-wikilinks.html

Example:

1
2
[[Default Path]]
[[My Note|Custom Display Text]]

Output:

1
2
<a href="https://ssp.sh/brain/default-path/">Default Path</a>
<a href="https://ssp.sh/brain/my-note/">Custom Display Text</a>

Configuration: Set brainBaseURL in your site params:

1
2
[languages.en.params]
brainBaseURL = "https://ssp.sh/brain/"

# 2. Images

Converts Obsidian-style image links to Hugo imgproc shortcodes for automatic image processing.

Source: /layouts/partials/process-images.html

Examples:

1
2
![[img_Why are we here on Earth_1745240448324.webp]]
![[my-screenshot.png|Alt text description]]

Output:

1
2
{ {< imgproc src="img_Why are we here on Earth_1745240448324.webp" caption="">}}
{ {< imgproc src="my-screenshot.png" caption="Alt text description">}}

Workflow: Simply copy your images from Obsidian’s attachment folder to your Hugo post’s bundle folder, and the image references will work automatically.

# 3. Admonitions

Converts Obsidian-style admonitions to styled warning/note boxes using the theme’s admonition system.

Source: /layouts/partials/process-admonitions.html

Supported Types:

  • - Note admonitions (blue styling)
  • - Abstract/summary admonitions
  • - Information admonitions
  • - Tip admonitions (green styling)
  • - Success admonitions (green styling)
  • - Question admonitions
  • - Warning admonitions (orange/red styling)
  • - Failure admonitions (red styling)
  • - Danger admonitions (red styling)
  • - Bug admonitions
  • - Example admonitions
  • - Quote admonitions

Examples:

Standard format (with empty line):

1
2
3
4
5
6
7
8
9
>  Important Notice
> 
> This is a warning message that will be displayed in a styled box.

>  Additional Information
> 
> This is a note with additional context.
> 
> Multiple paragraphs are supported.

Compact format (no empty line):

1
2
3
4
5
>  Quick tip
> This content appears directly after the title without an empty line.

>  Achievement unlocked
> This format also works for single-line content.

Output: Styled admonition boxes matching the theme’s design system.

# 4. Quotes

Normalizes blockquote formatting by removing unnecessary <p> wrappers for consistent styling.

Source: /layouts/partials/process-quotes.html

Example:

1
> this is a quote that does not look nice rendered

Without processing:

1
<blockquote><p>this is a quote that does not look nice rendered</p></blockquote>

After processing:

1
<blockquote>this is a quote that does not look nice rendered</blockquote>

# Processing Pipeline

All Obsidian features are processed in sequence in the layout templates:

Files: /layouts/posts/single.html and /layouts/preview/single.html

1
2
3
4
5
{{- $step1 := partial "process-images.html" (dict "Page" . "Site" .Site) -}}
{{- $step2 := partial "process-wikilinks.html" (dict "content" $step1 "Page" . "Site" .Site) -}}
{{- $step3 := partial "process-admonitions.html" (dict "content" $step2 "Page" . "Site" .Site) -}}
{{- $processedContent := partial "process-quotes.html" (dict "content" $step3 "Page" . "Site" .Site) -}}
{{- dict "Content" $processedContent "Ruby" $params.ruby "Fraction" $params.fraction | partial "function/content.html" | safeHTML -}}

Processing Order:

  1. Images: Convert ![[image.ext]] to Hugo imgproc shortcodes first (before wikilinks to avoid conflicts)
  2. Wikilinks: Convert [[Link]] to HTML links pointing to your second brain
  3. Admonitions: Convert > title to styled admonition divs
  4. Quotes: Normalize regular blockquote formatting

This processing order ensures that all features work together seamlessly. You can use wikilinks within admonitions, and images are processed before wikilinks to prevent conflicts.

# Uploading Example

# Long-form example of how I publish to Quartz


Link: My Obsidian Note-Taking Workflow - YouTube

# Short Asciinema Video

asciicast

# See Other Public Brains

Other Public Second Brains


Origin:
References: GoHugo Static Site Generators (SSG) Shared on SH: Hackernews, My Tech Stack
Created 2022-08-20