🚀 Migrando mi Obsidian Vault a un sitio web estático con Quartz 4, GitHub Pages y automatización total

En este artículo cuento el proceso completo que seguí para transformar mi vault de Obsidian en un sitio web estático moderno usando Quartz 4, desplegarlo en GitHub Pages, automatizar la subida con scripts, e integrar sincronización directa con Obsidian gracias al plugin Quartz Syncer.

Fue un camino lleno de decisiones, ajustes técnicos y descubrimientos útiles, así que aquí dejo todos los pasos documentados.
Ojalá le sirva a otros que quieran hacer algo similar.

🗂️ 1. Preparando el entorno

Mi objetivo era claro:

“Quiero publicar parte de mi Obsidian Vault como un sitio web, con control de versiones en GitHub, despliegue automático y un theme personalizado.”

Para esto usé:

  • MacBook Pro
  • Obsidian + mi theme personalizado
  • Quartz 4 (nuevo framework, no basado en Hugo)
  • GitHub Pages
  • (Opcional) Un servidor VPS con Plesk ( no llegué a usarlo, me animé por GitHub Pages)

📦 2. Instalando Quartz 4 en mi Mac

Quartz no se instala como un paquete global. Simplemente clonas la plantilla y trabajas dentro:

git clone https://github.com/jackyzha0/quartz.git mi-quartz
cd mi-quartz
npm install

Para ver el sitio localmente:

npx quartz build --serve

Quartz corre en http://localhost:8080.

🔗 3. Conectando Quartz con mi Obsidian Vault

Tenía mi vault en:

/Users/…/Nextcloud/obsidian-MHO2212

Y quería que Quartz usara la subcarpeta Public/ como contenido del sitio.
En Quartz, la carpeta que actúa como contenedor de notas es siempre:

mi-quartz/content

Probamos dos opciones:

✔️ A. Enlace simbólico (funcionó al inicio)

ln -s /Users/.../obsidian-MHO2212/Public content

Pero esto luego lo reemplazamos con Quartz Syncer, más adelante en el proceso.

🧹 4. Limpieza masiva del frontmatter

Muchas de mis notas tenían errores YAML como:

tags: []
  - mocs

Para limpiarlo, generamos un script Python que removió todas las líneas problemáticas:

import os
from pathlib import Path
 
# 👉 CAMBIA ESTA RUTA POR LA DE TU VAULT
VAULT_PATH = Path("AQUI LA RUTA")
 
def fix_file(path: Path):
    text = path.read_text(encoding="utf-8")
 
    # Solo nos interesa si tiene frontmatter YAML
    if not text.startswith("---"):
        return False
 
    lines = text.splitlines(keepends=True)
 
    # Localizar bloque de frontmatter (entre los dos primeros '---')
    fm_start = 0
    try:
        fm_end = next(
            i for i in range(1, len(lines))
            if lines[i].strip() == "---"
        )
    except StopIteration:
        return False  # frontmatter mal formado, mejor no tocar
 
    frontmatter = lines[fm_start:fm_end+1]
 
    # Comprobar si tiene tags: [mocs]
    has_inline_mocs = any("tags:" in l and "[mocs]" in l for l in frontmatter)
    if not has_inline_mocs:
        return False
 
    # Borrar líneas tipo "  - mocs" dentro del frontmatter
    new_frontmatter = []
    changed = False
    for l in frontmatter:
        stripped = l.strip()
        if stripped == "- mocs":
            changed = True
            continue  # saltamos esta línea
        new_frontmatter.append(l)
 
    if not changed:
        return False
 
    # Volver a montar el archivo
    new_lines = new_frontmatter + lines[fm_end+1:]
    new_text = "".join(new_lines)
 
    # Hacer copia de seguridad
    backup_path = path.with_suffix(path.suffix + ".bak")
    backup_path.write_text(text, encoding="utf-8")
 
    # Escribir el archivo corregido
    path.write_text(new_text, encoding="utf-8")
    return True
 
 
def main():
    count = 0
    for root, dirs, files in os.walk(VAULT_PATH):
        for name in files:
            if not name.endswith(".md"):
                continue
            p = Path(root) / name
            if fix_file(p):
                count += 1
                print(f"Arreglado: {p}")
    print(f"\nTotal de archivos modificados: {count}")
 
 
if __name__ == "__main__":
    main()
 

Esto resolvió la mayor parte de los errores, pero no permitió que se indexen en Quartz todo el vault (tampoco era lo que se quería)

🔧 5. Configurando Quartz 4 (quartz.config.ts)

Activé funciones avanzadas, incluyendo soporte para HTML embebido:

Plugin.ObsidianFlavoredMarkdown({
  enableInHtmlEmbed: true,
}),

Esto permite usar directamente:

<iframe src="..."></iframe>

Aunque descubrimos que Brave y algunos bloqueadores impiden que los iframes de LinkedIn carguen correctamente, pero esto es por la seguridad de los navegadores no de Quartz, la solución que halle fue permitir los iframe.

📤 6. Subiendo el sitio a GitHub

Inicializamos el repo real:

git init
git remote add origin https://github.com/marchelo2212/mi-quartz.git
git checkout -b v4
git add .
git commit -m "Inicializando Quartz"
git push -u origin v4

🚨 Importante:
GitHub ya no acepta contraseñas → usar Tokens (PAT).

🌐 7. Configurar GitHub Pages con Quartz

Usé el dominio:

https://marchelo2212.github.io

Configuración final del workflow (.github/workflows/deploy.yml):

on:
  push:
    branches:
      - v4
 
permissions:
  contents: write
  pages: write
 
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install
      - run: npx quartz build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: public
  deploy:
    needs: build
    permissions:
      pages: write
      id-token: write
    uses: actions/deploy-pages@v4

Cuando todo está bien, el sitio se despliega automáticamente cada vez que hago:

git add .
git commit -m "update"
git push

🤖 8. Automatizando con un script deploy.sh

Creamos un script que:

  • cambia a la rama correcta
  • hace pull
  • hace build
  • hace commit si hay cambios
  • sube a GitHub
#!/usr/bin/env bash
 
set -e
 
# RUTAS 
QUARTZ_REPO="Ruta-repo-PC/mi-quartz"
 
PAGES_REPO="Ruta-repo-github-pages/marchelo2212.github.io"
 
echo "🧭 Cambiando a repo Quartz..."
 
cd "$QUARTZ_REPO"
echo "🌿 Cambiando a rama v4..."
git checkout v4 # Mi main es V4
 
echo "🔍 Revisando cambios locales en Quartz..."
 
if [ -n "$(git status --porcelain)" ]; then
 
echo "📌 Hay cambios locales en Quartz. Haciendo commit..."
 
git add .
 
git commit -m "Cambios locales en Quartz antes de deploy"
 
else
 
echo "✅ No hay cambios locales en Quartz."
 
fi
 
echo "⬇️ Haciendo pull de los cambios que envió Quartz Syncer..."
 
git pull --rebase origin v4
 
 
echo "🧱 Generando sitio con Quartz..."
 
npx quartz build
 
echo "📦 Copiando resultado al repo de GitHub Pages..."
 
rm -rf "$PAGES_REPO"/*
 
cp -R public/* "$PAGES_REPO"/
 
echo "📤 Haciendo commit y push en marchelo2212.github.io..."
 
cd "$PAGES_REPO"
 
git add .
 
git commit -m "Deploy automático desde deploy.sh" || echo "ℹ️ No hay cambios nuevos para commitear en Pages."
 
git push
 
echo "✅ Deploy completado. Revisa https://marchelo2212.github.io"

🔁 9. Integración nativa con Obsidian: Quartz Syncer

En vez de usar enlaces simbólicos (primera opción que tomé), instalé el plugin:

👉 https://saberzero1.github.io/quartz-syncer-docs/

Este plugin permite:

  • elegir qué carpeta del vault se publica
  • sincronizar cambios automáticamente a GitHub
  • manejar frontmatter automáticamente
  • publicar/despublicar notas desde Obsidian

En mi caso configuré:

  • Repo: mi-quartz
  • Branch: v4 (Esto no está textual pero toma el repo main, por ello puse v4 como main)
  • Token: PAT con contents:write
  • Carpeta raíz: Public/

Esto reemplazó totalmente el enlace simbólico.

🎨 10. Personalización del theme

Quartz permite extender los componentes del layout editando:

quartz.layout.ts
quartz/styles/custom.scss

Agregué:

  • botones para ocultar/mostrar sidebars
  • ajustes de flex
  • colores adaptados a mi theme de Obsidian

Ejemplo:

Component.Flex({
  components: [
    { Component: Component.Search(), grow: true },
    { Component: Component.Darkmode() },
    { Component: Component.ReaderMode() },
  ]
})

Pueden ver los resultados en https://marchelo2212.github.io Aquí el repo de mi quartz, donde podrán ver las modificaciones hechas en él: GitHub - marchelo2212/mi-quartz: mi-quartz

✔️ 11. Resultado final

Ahora tengo un flujo así:

  1. Escribo notas en Obsidian → dentro de Public/
  2. Quartz Syncer detecta cambios → los envía al repo en v4 (main)
  3. Ejecuto mi deploy.sh. para llevar de quartz a mi sitio web
    1. GitHub Actions construye Quartz
    2. GitHub Pages actualiza el sitio
  4. El sitio queda publicado en:
    👉 https://marchelo2212.github.io Todo sin tocar terminal si no quiero.
    Y si quiero ajustes técnicos, puedo usar deploy.sh.

📌 Conclusiones y aprendizajes

  • Quartz 4 es muy flexible, pero requiere entender su estructura.
  • GitHub Pages + Actions da un flujo perfecto para sitios estáticos.
  • Obsidian + Quartz Syncer convierte tu vault en un CMS real.
  • Los bloqueadores como Brave afectan mucho los embeds.
  • Personalizar Quartz es tan simple como editar TS/SCSS.