obsidian-sync-station

obsidian-sync-station

Docker app from benjaminmue's Repository

Overview

Keeps an Obsidian vault synced using Obsidian's official headless Sync client (ob), with an optional local backup to your server storage. Configure everything from the web UI: Obsidian login (email/password/MFA), remote vault selection, optional end-to-end decryption password, and sync status. LAN-only by design: the web UI holds your Obsidian credentials, so do NOT expose it to the internet. Requires an active Obsidian Sync subscription. The proprietary ob client is installed from npm on first start (not bundled). Give the container its own dedicated vault path and never point the Obsidian desktop app at the same path. Backup: set Enable Backup to true and map the Backup path to enable scheduled tar.gz snapshots (schedule + retention configured in the web UI). Optional off-site backups via restic and ntfy notifications are supported.

Obsidian Sync Station

🤖 Built with AI — disclosed openly. This project is developed with heavy assistance from AI (Anthropic's Claude, via Claude Code): code, tests, and documentation. This is stated up front, not hidden. It's used for a personal homelab; review the code yourself before trusting it with your data, and treat it accordingly. Issues and PRs are welcome.

A small Docker container with a web UI that keeps an Obsidian vault synced using Obsidian's official headless Sync client — no LiveSync/CouchDB, no VNC/GUI container, no third-party sync protocol. Built for Unraid, runs anywhere Docker does.

Independent, unofficial project. Not affiliated with Obsidian. Requires an active Obsidian Sync subscription. See NOTICE.md.

Why

Obsidian Sync is proprietary: no self-hosted server, no REST API. In February 2026 Obsidian shipped an official headless client (obsidian-headless, the ob CLI) that runs the real sync from the command line. This project wraps it in a configurable web UI and packages it for a server, so a vault stays continuously synced and (optionally) backed up on your own hardware.

Features

  • Official, headless Obsidian Sync — no LiveSync/CouchDB, no VNC/GUI container.
  • Web UI (Obsidian-style dark/purple), LAN-only, gated by its own password.
  • Obsidian login (email / password / MFA), vault picker, optional end-to-end decryption password.
  • Two sync modes: continuous (live, ~30s) or every N minutes — supervised with auto-restart, live status and log tail.
  • Local backups (BACKUP=true): scheduled tar.gz snapshots with retention, run-now, and a snapshot list.
  • Off-box mirror (MIRROR=true): copy each snapshot to a second volume.
  • Encrypted off-site backups via restic (local path or cloud: S3/B2/SFTP).
  • Restore a snapshot to a safe staging area or over the live vault.
  • ntfy notifications on backup/sync events.
  • Unraid Community Applications template + prebuilt multi-arch image on GHCR.

The only thing not automated is listing it in the Unraid CA store (a manual, moderated forum step — see unraid/CA-SUBMISSION.md).

Install on Unraid

A Community Applications template lives in templates/obsidian-sync-station.xml.

Until it is submitted to the CA store, add it manually:

  1. Docker tab → Add Container → in Template paste: https://raw.githubusercontent.com/benjaminmue/obsidian-sync-station/main/templates/obsidian-sync-station.xml
  2. Adjust the paths (/vault should be its own dedicated share), set Enable Backup if you want backups (and map /backup), then Apply.
  3. Open the WebUI, set an access password, log in to Obsidian, pick your vault.

The image is published to GHCR: ghcr.io/benjaminmue/obsidian-sync-station:latest.

Sync mode

In the Sync card you can choose how syncing runs:

  • Continuous (default) — Obsidian's own live watcher (ob sync --continuous), which polls roughly every 30 seconds.
  • Every N minutes — the station runs a one-shot ob sync on a timer instead (set the interval, e.g. 5 minutes). Lighter, for people who don't need near-realtime sync.

Changing the mode re-applies immediately if sync is currently running.

Backups

Set BACKUP=true and map a /backup volume to your storage. A "Backup" card appears in the UI where you set the cron schedule (default 0 3 * * *) and how many snapshots to keep. Snapshots are tar.gz archives of the vault contents; older ones beyond the retention count are pruned automatically.

Mirror (off-box copy)

Set MIRROR=true and map a /mirror volume (a different disk or a remote share mounted on the host) to copy every new snapshot there as well, pruned by the same retention. For encrypted or cloud off-site copies, see restic.

Restore

Each snapshot in the UI has two restore actions:

  • To staging — extracts into /config/restores/<snapshot>/ for inspection. Safe; never touches the live vault.
  • To vault — extracts over the live vault. Destructive: sync is stopped and left stopped. When you restart sync, the restored state is pushed to Obsidian's remote and may overwrite newer changes. Requires an explicit confirmation.

Off-site backup (restic)

Set RESTIC_REPOSITORY and RESTIC_PASSWORD to also back the vault up to an encrypted restic repository — a local path, or cloud storage (S3, Backblaze B2, SFTP, …) via the usual restic backend env vars. It runs automatically after each local backup and keeps the newest N snapshots (same retention). A "Off-site backup (restic)" card in the UI shows status, snapshots, a run-now button, and restore-to-staging. The restic repo is encrypted independently of the vault's own encryption.

Notifications

Set an ntfy topic URL (NTFY_URL or in the UI) to get push notifications on successful backups and on backup/sync failures. Toggle each event in Settings. Public topics need no token; for an auth-protected ntfy server set an access token (NTFY_TOKEN or the masked field in the UI) — it's sent as a Bearer token.

Run it (local / any Docker host)

docker compose up --build

Then open http://localhost:8080, set an access password, log in to Obsidian, pick your vault, and start syncing.

Volumes

Container path Purpose
/config Persistent state: settings, GUI password hash, ob login + install
/vault The synced vault files (map to a dedicated share)
/backup Snapshot target (only when BACKUP=true)
/mirror Second snapshot target (only when MIRROR=true)

Environment

Variable Default Purpose
BACKUP false Enable backups + backup options in the UI
MIRROR false Also copy each snapshot to a second /mirror volume
RESTIC_REPOSITORY Enable encrypted off-site backups via restic (local path or cloud)
RESTIC_PASSWORD Encryption password for the restic repository
NTFY_URL Optional ntfy topic URL for push notifications (also settable in UI)
NTFY_TOKEN Optional ntfy access token (Bearer) for auth-protected servers (also settable in UI)
WEBUI_PORT 8080 Web UI port
DEVICE_NAME obsidian-sync-station Label in Obsidian Sync history

Important

  • Never run the Obsidian desktop app against the same vault path at the same time — the official client warns this causes conflicts. Give the container its own dedicated path.
  • With end-to-end encryption, ob decrypts locally and writes plaintext to /vault (and therefore to backups). Choose your storage accordingly.
  • Do not expose the web UI to the internet. It holds your Obsidian credentials.

How the proprietary client is handled

obsidian-headless is proprietary (published UNLICENSED). This image does not bundle it. The container installs it from the official npm registry on first start, into the /config volume. Only this project's own MIT-licensed code is distributed. See NOTICE.md.

License

MIT (this project's code only). See LICENSE and NOTICE.md.

Install obsidian-sync-station on Unraid in a few clicks.

Find obsidian-sync-station in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for obsidian-sync-station Review the template variables and paths Click Install

Related apps

Details

Repository
ghcr.io/benjaminmue/obsidian-sync-station:latest
Last Updated2026-07-21
First Seen2026-07-20

Runtime arguments

Web UI
http://[IP]:[PORT:8080]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Host port for the web UI (container listens on 8080). 8080 is often taken on Unraid, so this defaults to 8484 — change it if needed. LAN-only, do not expose to the internet.

Target
8080
Default
8484
Value
8484
ConfigPathrw

Persistent state: settings, GUI password, Obsidian login + the ob client install.

Target
/config
Default
/mnt/user/appdata/obsidian-sync-station
Value
/mnt/user/appdata/obsidian-sync-station
VaultPathrw

The synced vault files. Use a DEDICATED path only this container writes to. Never point the Obsidian desktop app here at the same time.

Target
/vault
Default
/mnt/user/obsidian-vault
Value
/mnt/user/obsidian-vault
BackupPathrw

Snapshot target on your server storage. Only used when Enable Backup is true.

Target
/backup
Default
/mnt/user/backups/obsidian
Value
/mnt/user/backups/obsidian
Enable BackupVariable

Set to true to enable scheduled backups and show backup options in the web UI.

Target
BACKUP
Default
false
Value
false
Device NameVariable

Label shown in the Obsidian Sync history.

Target
DEVICE_NAME
Default
unraid-sync-station
Value
unraid-sync-station
TimezoneVariable

Container timezone. Drives the backup schedule and all timestamps shown in the web UI.

Target
TZ
Default
Europe/Zurich
Value
Europe/Zurich
Mirror snapshotsVariable

Set to true to also copy each snapshot to a second destination (map the Mirror path).

Target
MIRROR
Default
false
Value
false
MirrorPathrw

Second snapshot destination. Only used when Mirror snapshots is true. Point this at another disk/share for an off-box copy.

Target
/mirror
Default
/mnt/user/backups/obsidian-mirror
Value
/mnt/user/backups/obsidian-mirror
ntfy URLVariable

Optional ntfy topic URL for push notifications (also settable in the web UI), e.g. https://ntfy.example.com/obsidian.

Target
NTFY_URL
ntfy tokenVariable

Optional ntfy access token (Bearer) for auth-protected ntfy servers. Leave empty for public topics. Also settable in the web UI.

Target
NTFY_TOKEN
restic repositoryVariable

Optional off-site backup via restic. Local example: /backup/restic. Cloud: s3:s3.amazonaws.com/bucket, b2:bucket:path, sftp:user@host:/path. For cloud, add the backend credential variables too (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, B2_ACCOUNT_ID/B2_ACCOUNT_KEY, ...) via Add another Path/Port/Variable.

Target
RESTIC_REPOSITORY
restic passwordVariable

Encryption password for the restic repository. Required when restic repository is set. Keep it safe — the repo cannot be read without it.

Target
RESTIC_PASSWORD