# Quartz Build and Deploy
Back to [[Obsidian/Self-Hosting with Quartz/Self-Hosting with Quartz|Self-Hosting with Quartz]].
## Where to build: Enceladus (recommended)
| | Build on Enceladus, copy `public/` to the NAS | Build on the NAS in Container Manager |
|---|---|---|
| Node | Already installed (v24.19.0, npm 11.17.0) | Needs a Node 22+ container |
| Vault access | Local `H:` (Google Drive) | Needs the vault synced to the NAS (Cloud Sync / Drive) |
| Speed | Fast desktop CPU | NAS CPUs are slow for about 2,000 notes plus images |
| Automation | Fits the existing Sunday routine and scripts | A separate scheduler and logs on the NAS |
| Downside | Enceladus must be on when the routine runs | More moving parts and harder debugging |
→ **Build on Enceladus** and mirror the finished `public/` folder to the NAS over SMB.
## Install (Quartz 5)
Quartz 5 needs **Node 22 or later and npm 10.9.2 or later** (https://quartz.jzhao.xyz/).
```powershell
cd C:\Users\jmars
git clone https://github.com/jackyzha0/quartz.git quartz-pubprojects
cd quartz-pubprojects
npm i
npx quartz create # pick a template, set baseUrl, choose an empty content folder
npx quartz plugin install --from-config
```
- Keep the Quartz folder **outside Google Drive**, because `node_modules` and `public/` shouldn't sync.
- This doesn't push anything to GitHub, and `npx quartz sync` isn't used.
## Configure: `quartz.config.yaml`
Quartz 5 configures in YAML (https://quartz.jzhao.xyz/configuration):
```yaml
configuration:
pageTitle: "J. Marshall Pittman"
baseUrl: notes.jmarshallpittman.org # no https://, no trailing slash
enableSPA: true
enablePopovers: true
locale: en-US
ignorePatterns:
- .obsidian
- .trash
- Templates
- "**/*.base" # until Bases rendering is tested
- "**/*.{mp4,mov,mkv,avi}"
```
- `ignorePatterns` is a backstop. The staging step below already keeps out anything unpublished.
- Quartz 5 has a **BasesPage** plugin (https://quartz.jzhao.xyz/features/bases), so `.base` files and ```` ```base ```` blocks may render. Test it in the preview. If they look wrong, keep them ignored and rely on the static `hub-list` and `folder-list` regions, which already work on the web.
- **`publish.css` doesn't apply in Quartz.** Quartz uses its own theme and custom styles, which may be needed to hide leftovers such as `folder-overview` code blocks or the "Live view (in Obsidian)" headings. The note properties panel is controlled by the NoteProperties plugin's `includedProperties`.
## Choosing what gets published
**Rule: the Obsidian Publish checkbox decides.** Don't add `publish: true`, because Obsidian Publish reads that property itself.
Options considered:
1. **Quartz filter plugins.**
- RemoveDrafts drops notes with `draft: true`. ExplicitPublish keeps only notes with `publish: true` (https://quartz.jzhao.xyz/features/private-pages). Neither one reads `dg-publish`.
- A custom filter is possible but means writing a plugin.
- **Quartz emits every non-Markdown file in `content/` regardless of filters.** Pointing Quartz at the whole vault would publish every image and video, including the ~762 MB of unpublished Events videos. ❌
2. **Staging copy (recommended).** ✅
- `quartz_stage.py` reads the same public Publish file list that `pubprojects_weblists.py` uses.
- It copies only the published `.md` notes, and the attachments those notes embed, into `content/`, keeping the folder structure.
- It uses the same 50% safety guard. If the list fails to load, it falls back to `dg-publish: true`, which `--sync-dg` keeps identical to Publish.
- Attachments come from the vault, not from Publish. If you later drop big image folders from Publish to save space, Quartz still carries them.
- It skips video files and any single file over 50 MB, and lists them in its report.
- It copies `Home.md` to `content/index.md` so it becomes the home page.
- It cleans `content/` first, so unpublishing a note removes it from the site on the next build.
## Build and preview
```powershell
cd C:\Users\jmars\quartz-pubprojects
python C:\Users\jmars\quartz_stage.py --apply # refresh content/
npx quartz build --serve # preview at http://localhost:8080
npx quartz build # production build into public/
```
## Deploy to the Synology
The Web Station site uses Apache 2.4, so add Quartz's `.htaccess` (from https://quartz.jzhao.xyz/hosting#using-apache) to `public/` before copying:
```apache
RewriteEngine On
ErrorDocument 404 /404.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI}.html -f
RewriteRule ^(.*)$ $1.html [L]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*)/$ $1/index.html [L]
```
Mirror the site to the NAS with `quartz_deploy.ps1`:
```powershell
robocopy C:\Users\jmars\quartz-pubprojects\public \\<NAS>\web\quartz /MIR /FFT /R:2 /W:5 /NP /LOG:C:\Users\jmars\quartz_deploy.log
```
- `/MIR` deletes files on the NAS that are no longer in `public/`. Always do a dry run with `/L` first, and never point it at anything but the site folder.
- `/FFT` handles the NAS's coarser timestamps, so unchanged files aren't copied again.
- The first deploy copies everything. After that, only changed files are copied.
- Robocopy's exit codes 0–7 mean success.
## Weekly automation (planned)
Sunday routine order:
1. `pubprojects_weblists.py --sync-dg --apply`
2. The storage check
3. `quartz_stage.py --apply`
4. `npx quartz build`
5. Robocopy deploy
The routine **skips the deploy** if the build fails or the staged note count drops by more than 50% from last week. The report will add a line with the Quartz site size and the deploy result.