📄 SKILL.md

← Vault

name: obsidian-vps-vault-deploy

description: Deploy an Obsidian vault on VPS as a browsable website with GitHub sync — custom Node.js server approach (NOT Quartz).

tags: [obsidian, vps, deploy, github, sync]

author: Rocha Sales / Hermes

date: 2026-06-13


Obsidian VPS Vault — Deploy and Sync

Deploy an Obsidian vault on a VPS as a browsable website, with GitHub sync.

Approach

Why NOT Quartz: Quartz v5 has build issues on VPS (symlink problems, Node22 requirement, complex dependency chain). It kept failing with enoent errors when content was symlinked.

The working approach: Custom Node.js server that serves .md files directly with directory listing and markdown rendering. Simple, reliable, full control.


Prerequisites

Fix — Two options:

Option A — Fast (if you can access GitHub):

1. Go to: https://github.com/OWNER/REPO/security/secret-scanning

2. Find the blocked secret and click "Unblock" or "Allow"

3. Push again

Option B — Create a clean repo:

`bash

1. Create new repo on GitHub (via API or web)

curl -s -X POST "https://api.github.com/user/repos" \

-H "Authorization: token GITHUB_TOKEN" \

-H "Content-Type: application/json" \

-d '{"name":"obsidian-vault-clean","description":"Clean vault","private":false}'

2. Change remote to new repo

cd /var/www/obsidian-vault

git remote set-url origin https://GITHUB_TOKEN@github.com/USER/new-repo.git

3. Push fresh (rewrite local history first if needed)

git filter-branch --force --tree-filter 'find . -name "*.md" -exec sed -i "s/REAL_TOKEN/REDACTED/g" {} \;' HEAD

git push --force origin main

`

Why filter-branch alone doesn't work: GitHub scans all commits in the push, including the pre-rewrite ones. The secret must be removed from the remote history entirely, which requires either unblocking on GitHub or force-pushing after a full history rewrite.


Nginx site not responding

Check: nginx -t && nginx -s reload

Check cert: certbot renew --dry-run

Server not starting (generic debugging)

`bash

1. Check systemd log

journalctl -u obsidian-site --no-pager -n 30

2. Check port

lsof -i :3099

3. Check if process is running

ps aux | grep obsidian

4. Manual start to see errors

/usr/bin/node /var/www/obsidian-site/index.js

`


File Transfer Tip

When updating the server script (e.g. adding features), use this sequence to avoid downtime:

`bash

1. Backup current

cp /var/www/obsidian-site/index.js /var/www/obsidian-site/index.js.bak

2. SCP new file to tmp

(write_file locally, then scp)

3. Move into place

mv /tmp/obsidian-index.js /var/www/obsidian-site/index.js

4. Restart (kills old process, starts new atomically)

systemctl restart obsidian-site

sleep 2

systemctl status obsidian-site --no-pager | head -10

`

Nginx site not responding

Check: nginx -t && nginx -s reload

Check cert: certbot renew --dry-run


File Transfer Tip

When writing configs to VPS, use scp instead of heredoc to avoid $ escaping issues:

`bash

write_file content to /tmp/file.conf locally

scp /tmp/file.conf root@VPS:/tmp/file.conf

ssh root@VPS "mv /tmp/file.conf /destination/"

`