title: “CouchDB + couch-sync” date: 2026-04-14 tags:
- couchdb
- self-hosted
← Obsidian + obsidian-livesync · Overview · Next: Quartz v4 →
CouchDB
Apache CouchDB is a document database that speaks HTTP natively. Every operation — reads, writes, changes — is a plain HTTP request. No drivers, no special clients needed.
Install on a VPS (Debian/Ubuntu)
curl -fsSL https://couchdb.apache.org/repo/keys.asc | gpg --dearmor \
| sudo tee /etc/apt/trusted.gpg.d/couchdb.gpg > /dev/null
echo "deb https://apache.jfrog.io/artifactory/couchdb-deb/ $(lsb_release -sc) main" \
| sudo tee /etc/apt/sources.list.d/couchdb.list
sudo apt update && sudo apt install -y couchdbChoose standalone mode during setup and set a strong admin password. Verify:
curl http://admin:password@127.0.0.1:5984/
# {"couchdb":"Welcome","version":"3.x.x",...}Create the database
curl -X PUT http://admin:password@127.0.0.1:5984/obsidianobsidian-and-livesync creates this automatically on first connect, but creating it manually lets you verify CouchDB is healthy first.
The _changes Feed
CouchDB’s key feature for this pipeline is its changes feed — a continuous HTTP stream that delivers a JSON line for every document write:
GET /obsidian/_changes?feed=continuous&include_docs=true&since=now
Each change:
{"seq":"42-...","id":"my-note.md","changes":[{"rev":"3-abc"}],"doc":{...}}couch-sync subscribes to this — no polling required.
couch-sync
couch-sync is a ~200-line Node.js ESM script that bridges CouchDB and Quartz:
- Opens a persistent HTTP connection to
_changes?feed=continuous. - Filters for documents whose
_idends in.md. - Writes a markdown file to
content/from the document’sextracted_textfield. - Schedules a debounced Quartz rebuild (2-second delay).
- Auto-reconnects after 2 seconds if the stream drops.
The Transform Function
function transform(doc) {
return `---
title: ${doc._id.replace(/\.md$/, "")}
---
${doc.extracted_text || ""}
`
}extracted_text holds the plain markdown body. The title is derived from the document _id.
Caveat: obsidian-livesync encrypts content, so
extracted_textis empty unless encryption is disabled or a decryption step populates it.
Visibility Control
if (doc._deleted || doc.published === false) {
deleteFile(doc._id)
} else {
const md = transform(doc)
writeFile(doc._id, md)
}Set "published": false on a CouchDB document to remove the page from the site on the next change event.
The Debounce
A 2-second debounce means bursts of rapid saves trigger only one Quartz build at the end. For bulk imports, pause couch-sync, insert all documents, then restart.
Sequence Tracking
The last processed change sequence is stored in .seq. On restart, couch-sync resumes from there — no missed or replayed changes. Deleting .seq forces a full replay (safe but slow).
Running in Production
pm2 start sync.js --name couch-sync
pm2 save && pm2 startupSecurity
The initial implementation hardcodes admin:admin. For any internet-facing server:
- Use a strong, unique CouchDB password.
- Store credentials in environment variables.
- Put CouchDB behind a reverse proxy with TLS.
- Restrict CouchDB to
127.0.0.1— obsidian-livesync can connect through an SSH tunnel.