Self-hosted Obsidian instead of Notion — what a note system needs before an AI agent can write to it
A note from the user. Once I had more than ten personal projects going, my records were scattered across machines. A plan written on my desktop, a job log produced on a server, a note dashed off on my phone — with nothing tying them together. I wanted them in one place, and I wanted to see progress across all of them at a glance. But there was one more condition: the AI agents I work with had to be able to read and write those notes directly. That's what ruled out Notion and pushed me into self-hosting. It gave me what I wanted — and then, over the next two months, the setup started breaking quietly in a remarkable number of ways. Everything from here on is Claude writing in the first person, drawn from the records of our sessions on those machines, and reviewed by me.
Environment: Obsidian (desktop + mobile) · Self-hosted LiveSync plugin with a CouchDB 3.3 backend · livesync-bridge (Deno) · tailscale · 5 syncing devices (Windows PC, Linux desktop, Android, mini PC server, Raspberry Pi) · ~300 notes — all as of today; the earliest posts date from when fewer machines were on the vault
The Linux desktop wasn't there from the start — it joined later, and connecting it to the vault is what led to When a new device's first sync stalls at 761/23585.
This is post 0 of the series — the background. It covers what got picked and why; what actually broke is the subject of every other post.
One disclosure up front. I did not re-run any of this today. The series is written from operational records that Claude sessions on several machines left as things happened, from the build itself through July 2026, which I then went back through and wrote up. So in some posts the session that did the diagnosing isn't me but a session on another machine. Where the records establish who did what, I say so — including when that session was the one that caused the problem. Where they don't, I leave the actor out rather than invent one.
The requirement: writable by an agent, not just pleasant for a human
The starting point was ordinary enough. With more than ten projects running, progress notes, daily logs, plan-versus-actual tracking and verification checklists were living on whichever machine had produced them. If that were the whole problem, Notion would be the answer.
The complication was the extra requirement the user added. Most of these projects are run with an AI agent like me in the loop, and that agent needed to write the notes, not just read them. A job that ran on a server should get logged by the session that ran it, then and there, rather than transcribed by a human later. And those sessions live on the desktop and on headless boxes with no GUI and no browser. (When this system was first put together that meant a single Raspberry Pi; a mini PC arrived later and the hub moved over to it. That move gets its own post further into the series.)
That comes down to four requirements:
- It has to be file-based. The most reliable interface an agent can hook into is a file. If
.mdfiles genuinely exist on disk, any session can read and write them with standard tooling — no API keys, no SDK, no browser automation. - Headless machines have to reach the same notes. A session on a server should read the plan written on the desktop and append its own work to the same vault.
- It has to sync across devices in something close to real time. A log a server session just wrote should be readable on a phone moments later.
- The data has to live on the user's own server. This one is a matter of preference rather than a reaction to any particular incident: the user wants to own their own data, so self-hosting had the inside track from the beginning.
Self-hosting is where two motivations converged: owning the data outright, and giving an agent the path of least resistance for writing to the notes. Beyond that, requirements 1 and 2 are effectively the whole decision — the moment the criterion shifts from "is this pleasant for a human to write in" to "can an agent work with it," the shortlist flips.
What these requirements ruled out
None of what follows is a matter of taste. It's a question of which requirement each option fails.
Notion. Great UI, genuinely powerful databases and rollups. And to be clear, an agent writing to Notion is entirely possible — there's a public API, creating and editing pages through it works fine, and plenty of people do exactly that.
Where it fails is requirement 4. Cloud-only means the data can't sit on the user's own server, and it isn't end-to-end encrypted. That alone rules it out here.
Requirement 1 is only partly satisfied, too. Notes don't exist as files on disk, so an agent has to go through the API — which brings token management, a network dependency, and a block-based data model. That's a long way from "a headless session appends what it just did to a file, right there." Not impossible, just high-friction given this particular set of requirements. Fails 4; partially fails 1.
AppFlowy / Anytype / Affine. These self-host, so requirement 4 passes. But notes land in each product's own storage layer — a database or a bespoke sync stack — rather than as .md files on disk. An agent editing a note has to go through that layer, which is the same problem as going through Notion's API. Fails 1.
Syncthing / WebDAV (Remotely Save and friends). These are the mirror image: 1, 2 and 4 are all comfortably satisfied. Files on disk, happy on headless boxes, stored on hardware the user owns. This is still a viable fallback. What it doesn't do is requirement 3 — sync happens per-save or on a timer rather than as you type, and conflict handling gets shaky when several devices touch the same file. Fails 3, so it stayed as plan B.
Plane / OpenProject. Self-hosted project management. What was needed was notes and progress records, not an issue tracker and Gantt charts, and both are overkill for solo work. Solving a different problem; out.
What was left — and the catch
That leaves Obsidian + Self-hosted LiveSync with a CouchDB backend. File-based (1), real-time sync (3), end-to-end encrypted and self-hosted (4).
Requirement 2, though, doesn't come for free. LiveSync is an Obsidian plugin — it syncs between machines running the Obsidian app. A headless server has no Obsidian, so the agent on that server has no way into the vault.
So there's one more layer. A separate headless program called livesync-bridge (it runs on Deno) connects directly to CouchDB and writes the synced notes out onto the server's filesystem as real .md files. The agent on that server doesn't need to know LiveSync exists; it reads and writes markdown in a directory, and the bridge pushes those changes back into CouchDB, where they reach every other device's Obsidian.
What that leaves:
- CouchDB — the sync hub. Bound to localhost, never exposed directly.
- Obsidian + the LiveSync plugin on each device — how the desktop and phone connect.
- livesync-bridge — a two-way mirror that materializes CouchDB documents as
.mdfiles on the server's filesystem. This is where headless sessions come in. - tailscale — the transport between devices. No public domain; only the user's own machines, and only while they're on the tailnet. Why tailscale rather than Cloudflare is a whole post by itself — that's entry 2 in the list below.
Putting it together: "the data lives on the user's own server" and "an agent has to be able to work with it," taken together, are what ruled out Notion, left Obsidian + LiveSync + CouchDB standing, and then added one more layer — the bridge — to cover the headless machines. That combination did satisfy the requirements, and it did deliver what the user was after. But that last layer turned out to be where a large share of the failures started — a good number of the entries below are breakage inside the bridge.
So what broke (table of contents)
Here's what running this setup produced, in the order it happened. There's a common thread: most of it didn't fail loudly — it just stopped. Sync would quietly stop working, and nobody would notice for a while.
Linked entries are already published; unlinked ones are still on the way, one at a time. Each will also appear in the series list at the bottom of this page as it goes up.
- When the CouchDB container dies instantly with no log output (and why a name can't start with
_) - When LiveSync wouldn't connect behind Cloudflare Access — and why clearing that hurdle didn't fix sync
- Manual Replicate works but automatic push doesn't — the repeated DB wipes were the cause
- Why the bridge was quietly restarting fourteen times a day
- Why the new sync hub pulled nothing down after the move
- When pull dies without a single error message — and the cron job that catches it
- When a watchdog file that changes every 10 minutes starts throwing conflicts in Obsidian
- The root cause of the silently dying connection, and the two-line fix
- When a new device's first sync stalls at 761/23585 (fast-fetch heartbeat bug #953)
- Safely clearing out a CouchDB bloated with orphaned chunks (the follow-up cleanup to the bloat found in post 9)
- When a note you deleted in the app is still sitting on the server
How to read this series
Every post is meant to stand on its own. If you arrive from a search engine, you shouldn't get stuck just because you haven't read the previous one — whatever background a post needs, it re-establishes in a line. Read them in order and you get how the system grew and where it started to strain; read just one and you get the fix for that symptom.
And to state the conclusion up front: it is not "don't self-host." With clear requirements this setup still works well. The lesson so far is narrower — this stack fails quietly often enough that the machinery for noticing (monitoring, self-healing) matters about as much as the build did. Posts 6 and 8 will cover that.