Disclaimer
Is this project open source? Yes
Is this project completely free? Yes
Is this project made with AI beyond the author’s ability to comprehend how it works? No
On Android, the vault only syncs while the Obsidian app is open and in the foreground. Nothing changes on the phone until you look at it, and then it catches up while you wait. So I’d pick up the phone, open a note, and read something from two days ago without knowing it. Sometimes the sync is fast enough, but sometimes, I have a lot of changes piled up so it takes me a lot time to see the change at all.
The fix is Obsidian Sync running as a background process in Termux, independent of the Obsidian app: the phone stays current whether or not I’ve opened it. Short instructions first, then why each piece is there. It is Android only - there’s no equivalent on iOS as far as I know.
Tip: If this seems overly complicated, consider requesting Claude to help you with following this post.
Setup, short version
1. Install these apps from F-Droid: Termux, Termux:Boot, Termux:API. (The Play Store builds are unmaintained and can’t talk to each other.) Obsidian itself comes from the Play Store as usual.
2. Let Termux run in the background: Settings → Apps → Termux → Battery → Unrestricted. Then open Termux:Boot once, so it registers itself for boot.
3. In Termux, install the client:
pkg install nodejs termux-api -y
npm install -g obsidian-headless
termux-setup-storage
4. Log in and link your vault folder:
VAULT=/storage/emulated/0/Documents/obsidian
ob login
ob sync-list-remote # find your remote vault
ob sync-setup --path "$VAULT" # pick it
ob sync-config --path "$VAULT" --excluded-folders _sync # only needed for the status note below
ob sync --path "$VAULT" # first full pull, takes a while
5. Open Obsidian and point it at that folder as an existing vault.
6. Run it in the background on every boot. Save the script below as ~/obsync.sh, then:
chmod +x ~/obsync.sh
mkdir -p ~/.termux/boot
printf '#!/data/data/com.termux/files/usr/bin/bash\nexec "$HOME/obsync.sh"\n' > ~/.termux/boot/start-obsync.sh
chmod +x ~/.termux/boot/start-obsync.sh
~/obsync.sh & # start it now, without waiting for a reboot
That’s the whole thing. Everything below is why.
The piece that makes this possible: obsidian-headless
If you haven’t seen it: obsidian-headless is a CLI client for Obsidian Sync and Publish, from the Obsidian team. It’s a normal npm package, so it runs anywhere Node runs - including Termux on Android.
ob sync --continuous keeps running and syncs as changes arrive. Point the Obsidian app at the same folder and the app becomes a viewer over a folder something else keeps up to date.
Making it survive boot and Android’s battery management
--continuous is not enough on its own. Android will freeze the process, and if the sync child dies nothing brings it back. So the thing I actually run is a small supervisor loop:
termux-wake-lockat the start, plus the Unrestricted battery setting from step 2. Without both, the process gets frozen within minutes.- The loop starts
ob sync --continuousas a child, then every 60 seconds checks it’s still alive and restarts it if not. In practice it rarely needs to - but the one time it died silently is what made me write this part. - The log rotates itself past 5 MB. Crude, but a log growing forever on a phone is its own bug.
- Termux:Boot starts the whole thing after the first unlock following a reboot.
The supervisor loop
#!/data/data/com.termux/files/usr/bin/bash
export PREFIX=/data/data/com.termux/files/usr
export PATH="$PREFIX/bin:$PATH"
# Termux:Boot gives a bare env; ob's "#!/usr/bin/env node" shebang needs this
export LD_PRELOAD="$PREFIX/lib/libtermux-exec.so"
VAULT=/storage/emulated/0/Documents/obsidian
termux-wake-lock
start_sync() {
node "$PREFIX/bin/ob" sync --path "$VAULT" --continuous >> "$HOME/obsync.log" 2>&1 &
echo $! > "$HOME/obsync.sync.pid"
}
start_sync
while true; do
pid=$(cat "$HOME/obsync.sync.pid")
kill -0 "$pid" 2>/dev/null || { echo "[$(date)] died, restarting" >> "$HOME/obsync.log"; start_sync; }
# ... write the status note here ...
sleep 60
done
Bonus: a status note you can read inside Obsidian
Not essential, but it’s the part I ended up looking at most. A background process you can’t see is a background process you don’t trust, so on each pass the same loop writes a note into the vault, and I open it in Obsidian like any other note:
# Sync status - running
Heartbeat: **2026-09-20 13:58:11**
> If the heartbeat above is more than ~2 minutes behind the current time,
> sync is **not** running and this vault may be stale.
| | |
|---|---|
| Sync process | **running** (pid 17083) |
| Last sync activity | 2026-09-20 13:57:40 (31s ago) |
| Downloaded this run | 412 |
| Uploaded this run | 37 |
| Skipped, over 5 MB | 3 |
| Errors | 0 |
| Battery | 74% |
## Recent activity
...last 20 log lines...
The heartbeat is the whole trick. A table of counters can’t tell you the process is dead, because a dead process leaves its last numbers sitting there looking healthy. A timestamp that stops advancing tells you immediately, and it needs no plugin to read - Obsidian renders it, and I compare it to the clock in the status bar.
The counters come from grepping the sync log (Downloaded, Upload complete, File too large), and the battery from termux-battery-status in Termux:API. Not elegant, but it’s a grep -c per line of information.
This is why step 4 sets --excluded-folders _sync: the note lives in _sync/, and without the exclusion each device’s status note syncs to every other device and you end up reading the phone’s heartbeat on the desktop. Excluded means it stays device-local, which is exactly what a device status note should be.
Caveats
- Files over 5 MB are never transferred by Obsidian Sync. They show as
File too largeand are silently absent on the phone. That’s why the status note counts them and lists them - large attachments live outside my vault (in my case on Drive, referenced by URL). - Config sync is off in my setup. Syncing
.obsidianbetween a desktop and a phone was creating conflict files, and I don’t need my desktop layout on a phone. - Conflict strategy is
merge. With several writers on the same vault I’d rather have a merged note than async-conflictfile, but that is a taste thing and it’s worth deciding on purpose rather than by default. - Battery - I haven’t measured a meaningful hit with the phone charging overnight and a wake lock held, but I also wouldn’t ship this to someone who nurses their battery.
Happy to share the full script if anyone wants it - it’s about 80 lines and most of them are building that markdown table. And if someone has found a cleaner way to keep the process alive than a supervisor loop and a wake lock, I’d like to hear it.