I manage two distinct public websites from a single private Obsidian vault. One is a digital garden showcasing my notes and research at brain.rakshanshetty.in, and the other is a traditional blog at rakshanshetty.in. Both sites are powered by Quartz, deployed to Cloudflare Pages, and published from the same Obsidian vault.

This architecture solves a problem I faced: how do you publish different content to different audiences while maintaining a unified knowledge base? The solution combines tag-based content filtering, upstream-safe customization patterns, and git submodule workflows.

TLDR

Why Multi-Site Publishing from Obsidian?

The Knowledge Management Dilemma

I use Obsidian for personal knowledge management, and I wanted to publish some content publicly, but not everything. I wanted a blog separate from my personal digital garden, with different sites for different audiences and content quality while maintaining a unified knowledge base.

Traditional solutions I considered fell short:

  • Separate Obsidian vaults: Fragments your knowledge, breaks connections between notes, creates maintenance overhead
  • Obsidian Publish: Excellent product but limited customization
  • Manual export workflows: Tedious copy-paste, error-prone, breaks automation
  • My blog has evolved from ghost to gatsby now finally to quartz

The Multi-Site Solution

What if you could:

  1. Write everything in one private Obsidian vault
  2. Tag notes for specific destinations (blog, brain, or both)
  3. Deploy to multiple sites with automated selective publishing
  4. Customize each site independently

This is exactly what tag-based selective publishing with Quartz enables. It’s about maintaining a unified knowledge graph while presenting different facets to different audiences, with full control over the entire pipeline.

Why Quartz?

Quartz is a static site generator built specifically for Obsidian-style markdown. Quartz preserves the knowledge graph, renders backlinks natively, and supports Obsidian-flavored syntax out of the box.

The real advantage is what I call the upstream-safe customization pattern. Most static site generators force you to choose between forking the entire codebase (maintenance nightmare) or living with limited customization. Quartz offers a third path: isolated customization that survives framework updates. I’ll show you how this works later, but it fundamentally changes how I build custom components and plugins.

Architecture Overview

How Content Flows from Vault to Sites

Here’s how content moves from your private vault to multiple public sites:

flowchart TD
    A[Private Obsidian Vault<br/>~/obsidian-vault] --> B{Tag-Based Filter}
    B -->|tags: blog| C[Blog Content Repo<br/>git submodule]
    B -->|tags: brain| D[Brain Content Repo<br/>git submodule]
    B -->|no tags| E[Private Content<br/>]

    C --> F[Quartz Build<br/>Blog Site]
    D --> G[Quartz Build<br/>Digital Garden]

    F --> H[Cloudflare Pages<br/>rakshanshetty.in]
    G --> I[Cloudflare Pages<br/>brain.rakshanshetty.in]

    style A fill:#4C566A,stroke:#5E81AC,stroke-width:2px,color:#ECEFF4
    style B fill:#5E81AC,stroke:#81A1C1,stroke-width:2px,color:#ECEFF4
    style C fill:#D08770,stroke:#BF616A,stroke-width:2px,color:#ECEFF4
    style D fill:#D08770,stroke:#BF616A,stroke-width:2px,color:#ECEFF4
    style E fill:#BF616A,stroke:#96505A,stroke-width:2px,color:#ECEFF4
    style F fill:#8B6F5F,stroke:#7C5F4F,stroke-width:2px,color:#ECEFF4
    style G fill:#8B6F5F,stroke:#7C5F4F,stroke-width:2px,color:#ECEFF4
    style H fill:#6B8E7F,stroke:#5F7F70,stroke-width:2px,color:#ECEFF4
    style I fill:#6B8E7F,stroke:#5F7F70,stroke-width:2px,color:#ECEFF4

The Key Components

1. Single Source of Truth

Your private Obsidian vault remains the canonical source for all content. You write, organize, and connect notes naturally in Obsidian without worrying about publishing logistics.

2. Tag-Based Selective Publishing

Each note’s frontmatter tags determine its publishing destination:

---
title: My Blog Post
tags: [blog, javascript]
---

This note publishes to the blog site. A note with tags: [brain] goes to the digital garden. A note with tags: [blog, brain] publishes to both sites.

3. Customization Isolation

This is where Quartz really shines. All customizations live in a quartz-custom/ directory, never modifying core Quartz files. When Quartz releases a new version, you copy in the updated core files and your customizations remain untouched. No merge conflicts, no manual reconciliation, no broken components.

4. Git Submodules for Content Separation

Each site links its content as a git submodule. Content has its own git history independent of framework changes—you can roll back content without affecting site configuration, or update Quartz without touching content.

Tag-Based Content Syncing

The content sync script scans your vault for markdown files, parses frontmatter to check tags, and copies matching files to the appropriate content repository.

Take a look at the sync code: utils/content-sync/index.js

This flexibility is powerful. You can:

  • Keep private notes in the same vault alongside public content
  • Share important content across sites when appropriate
  • Easily change publishing targets by modifying tags
  • Maintain a single knowledge graph with selective exposure

Upstream-Safe Customization Pattern

This is where Quartz differentiates itself from traditional static site generators. The problem with most frameworks is customization lock-in: modify core files, and you’re essentially maintaining a fork. Update the framework, and your customizations break.

Quartz offers an alternative: isolated customization.

The Core Principle

Never modify files in the quartz/ directory. All customizations—components, plugins, styles—live in quartz-custom/ and are imported via configuration files.

Your project structure looks like this:

my-blog/
├── quartz/                    # Upstream Quartz (never modify)
├── quartz-custom/             # All your customizations
│   ├── components/
│   ├── plugins/
│   │   ├── transformers/
│   │   └── emitters/
│   ├── styles/
│   ├── utils/
│   └── static/
├── content/                   # Git submodule
├── quartz.config.ts           # Configuration imports from quartz-custom/
├── quartz.layout.ts           # Layout imports from quartz-custom/
└── package.json

Integration Points

The magic happens in two files: quartz.config.ts and quartz.layout.ts. These files import from both core Quartz and your custom directory:

// quartz.config.ts
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
import * as CustomPlugins from "./quartz-custom/plugins"
 
const config: QuartzConfig = {
  plugins: {
    transformers: [
      Plugin.FrontMatter(),
      Plugin.SyntaxHighlighting(),
      Plugin.ObsidianFlavoredMarkdown(),
      // Custom plugins seamlessly integrate
      CustomPlugins.RemoveTags({ tags: ["blog"] }),
      CustomPlugins.Img(),
    ],
    emitters: [
      Plugin.ContentPage(),
      Plugin.Assets(),
      // Custom emitters work the same way
      CustomPlugins.CustomStyles(),
    ],
  },
}
// quartz.layout.ts
import * as Component from "./quartz/components"
import * as CustomComponent from "./quartz-custom/components"
 
export const sharedPageComponents = {
  header: [],
  footer: CustomComponent.Footer(),
}
 
export const defaultContentPageLayout = {
  beforeBody: [
    Component.Breadcrumbs(),
    CustomComponent.ArticleTitle(),
    CustomComponent.ContentMeta(),
  ],
  right: [
    Component.DesktopOnly(Component.TableOfContents()),
    Component.Backlinks(),
  ],
}

For complete examples, see:

The Safe Update Process

When Quartz releases a new version, updating is remarkably simple:

# Clone or pull latest Quartz upstream
cd ../quartz
git pull origin v4
 
# Run update command (from justfile)
cd ../my-blog
just quartz-update

The update command:

  1. Removes quartz/ directory and core config files
  2. Copies fresh versions from upstream
  3. Reinstalls dependencies
  4. Leaves quartz-custom/ completely untouched

No merge conflicts. No manual reconciliation. No broken customizations.

Site-Specific Customization

The architecture supports different looks and feels for each site while sharing components where appropriate.

Layout Differences

Here’s how layouts differ between sites:

Blog layout (clean, focused on reading):

// quartz.layout.ts for rakshanshetty.in
export const defaultContentPageLayout = {
  beforeBody: [
    Component.Breadcrumbs(),
    CustomComponent.ArticleTitle(),
    CustomComponent.ContentMeta(),
  ],
  left: [],  // No left sidebar for cleaner blog layout
  right: [
    Component.DesktopOnly(Component.TableOfContents()),
    Component.RecentNotes({ title: "Recent Posts" }),
  ],
  afterBody: [
    CustomComponent.PrevNextNav(),  // Previous/Next navigation
  ],
}

Digital garden layout (exploratory, emphasizes connections):

// quartz.layout.ts for brain-public
export const defaultContentPageLayout = {
  beforeBody: [
    Component.Breadcrumbs(),
    CustomComponent.ArticleTitle(),
    CustomComponent.ContentMeta(),
  ],
  left: [
    Component.PageTitle(),
    Component.Search(),
    Component.Darkmode(),
    Component.Explorer(),  // Folder navigation
  ],
  right: [
    Component.Graph(),  // Knowledge graph
    Component.DesktopOnly(Component.TableOfContents()),
    Component.Backlinks(),
    Component.RecentNotes({ title: "Recent Notes" }),
  ],
}

The blog emphasizes linear reading (previous/next navigation, minimal sidebar). The digital garden emphasizes exploration (graph view, folder navigation, backlinks).

Automatic Metadata Generation

Use frontmatter for rich metadata:

---
title: "Your Blog Post Title"
description: "Clear, concise description for search results"
date: 2024-01-15
tags: [tutorials, nodejs, web-development]
---

Internal linking:

  • Use Obsidian wikilinks naturally
  • Quartz converts to proper HTML links
  • Creates interconnected knowledge graph
  • Search engines love internal link structure

This system lets me maintain a unified vault while publishing selectively to multiple sites. Write once in Obsidian, tag intentionally, and let the architecture handle the rest. If you’re facing the same challenge, this approach offers a path forward.

Happy publishing!

TL;DR

The Problem: I wanted to publish different content from my Obsidian vault to different websites (blog, digital garden) without maintaining separate vaults.

The Solution: Tag-based selective publishing with Quartz static site generator, git submodules for content separation, and automated deployment to Cloudflare Pages.

How It Works:

  1. Single Obsidian vault - Write everything in one place
  2. Tag notes - Add tags: [blog] or tags: [brain] to frontmatter
  3. Sync script filters - Node.js script copies matching notes to site-specific content repositories (git submodules)
  4. Quartz builds - Generates static sites with full Obsidian feature support (wikilinks, backlinks, graph view)
  5. Deploy with one command - just deploy syncs, builds, commits, and triggers Cloudflare Pages deployment

Key Architectural Advantages:

  • Upstream-safe customization: All custom components/plugins live in quartz-custom/ directory, never modifying core Quartz files. Framework updates don’t break customizations.
  • Git submodules: Content and framework code have separate version control, enabling independent rollbacks and clear change auditing.
  • Flexibility: Cross-publish by using multiple tags (tags: [blog, brain]). Unpublish by removing tags.

Reference Code:


Resources:

References