While setting up Quartz as described in the Quartz docs, the native way intrinsically supports the idea of separating the content from the code, by creating a soft link between the two. We’ll use a submodule to further achieve this idea.

Why submodules

There are several reasons that make you wanna try putting your /content separately in a (private) Git repository.

  1. You are editing your contents in Git and you don’t want to mix up the commits of /content with the updates of the codebase.
  2. Your contents are private. But for some reason, you have to make the blog repository public (e.g. when you want to host on GitHub Pages for free, or when you choose to use Giscus as comment manager).
  3. Quartz allows you to label your posts with draft: true in frontmatter to keep them from showing up. But a opened-up /content will spoiler what you’re working on.

We are working with Git Submodules and Cloudflare Pages in this tutorial. We’d use Cloudflare Access to fortify sensitive areas of your blog.

Prerequisites

Create a submodule

Make sure you’ve got your blog ready. Then create a GitHub repository for your content and replace \content.

cd quartz
# remove the existing folder or soft link
git rm --cached ./content
# replace my HTTPS URL with yours
git submodule add --force https://github.com/felixnie/Obsidian-Draftz.git content
git submodule update --init --recursive

After each commit of the content, retrieve updates by running git submodule update --recursive --remote.

If you decide to set your submodule repository to private, a new SSH key is necessary for Cloudflare Pages to access your submodule. The standard HTTPS submodule URL relies on interactive username/password input, which isn’t possible in an automated build environment.

To change the HTTPS URL to SSH URL, you can do it either manually or via CLI.

quartz/.gitmodules
[submodule "content"]
    path = content
    url = git@github.com:felixnie/Obsidian-Draftz.git   
cd draftz
# edit url
git config --file=.gitmodules submodule.content.url git@github.com:felixnie/Obsidian-Draftz.git

Commit after the update.

# commit changes
git add .gitmodules
git commit -m "Switch submodule to SSH URL"
git push

Generate a SSH key

Create a new SSH key on your local machine. This will generate a pair of private and public keys, namely id_ed25519 and id_ed25519.pub.

# save as id_ed25519
ssh-keygen -t ed25519 -C "Git deploy key for Cloudflare/GitHub Pages"

Set up GitHub deploy key

Then let the public key in id_ed25519.pub be the deploy key of your private content repository. Go to Settings → Deploy Keys → Add deploy key and paste everything but the comments (if you have any).

Make sure “Allow write access” is enabled.

Deployment

Cloudflare Pages

After setting up following the guide, we shall go through some extra steps to set up the environmental variables.

Add the following 2 variables to your build environment in Cloudflare Pages:

VariablesValues
GIT_DEPLOY_KEYThe entire content of your private SSH key, including the BEGIN OPENSSH PRIVATE KEY and END OPENSSH PRIVATE KEY markers
GIT_SSH_COMMANDssh -i $GIT_DEPLOY_KEY -o StrictHostKeyChecking=no

The value of GIT_DEPLOY_KEY is encrypted and will not get exposed in build logs. Remember to deal with private keys under trusted Internet environments.

GitHub Pages

In your repository, simply add the deploy workflow but with GIT_DEPLOY_KEY injected. Here’s how.

quartz/.github/workflows/deploy.yaml
name: Deploy Quartz site to GitHub Pages
 
on:
  push:
    branches:
      - v4
 
permissions:
  contents: read
  pages: write
  id-token: write
 
concurrency:
  group: "pages"
  cancel-in-progress: false
 
jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Fetch all history for git info
          submodules: recursive # mod: submodules
          ssh-key: ${{ secrets.GIT_DEPLOY_KEY }} # mod: ssh key
          persist-credentials: false  # mod: disable HTTPS token
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Install Dependencies
        run: npm ci
      - name: Build Quartz
        run: npx quartz build
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: public
 
  deploy:
    needs: build
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Then add GIT_DEPLOY_KEY in Settings → Secrets and variables → Actions → Repository secrets.

What’s next

Quartz is built to be shared, not exclusive. It’s also not easy to completely hide a post. If you’re looking for some tweaks to make your pages more private, check Creating a Secret Garden.