Add CouchDB and livesync-bridge for Obsidian LiveSync

CouchDB is served at livesync.${DOMAIN} via Traefik without a middleware
chain, since CouchDB handles CORS itself. livesync-bridge mirrors the
vault to ~/repo/notes, and notes-autocommit.sh pushes it from cron.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Simon 2026-10-18 23:55:30 +02:00
parent 8e6866fb20
commit c64be0ab02
4 changed files with 104 additions and 0 deletions

View File

@ -30,6 +30,10 @@ secrets:
file: $SECRETSDIR/carconnect_grafana_pw
home_assistant_token_spoolman:
file: $SECRETSDIR/home_assistant_token_spoolman
couchdb_password:
file: $SECRETSDIR/couchdb_password
livesync_bridge_config:
file: $SECRETSDIR/livesync_bridge_config.json
include:
@ -909,6 +913,56 @@ services:
- "traefik.http.routers.carconnect-grafana.tls.certresolver=milvert_dns"
- "traefik.http.routers.carconnect-grafana.tls=true"
couchdb:
image: couchdb:3.5
container_name: couchdb
restart: unless-stopped
entrypoint: ["/bin/sh", "/couchdb-entrypoint.sh"]
networks:
- backend
environment:
- TZ=${TZ}
- COUCHDB_USER=obsidian
secrets:
- couchdb_password
volumes:
- ${DIR_LOCAL}/couchdb/data:/opt/couchdb/data
- ./obsidian-livesync/couchdb-entrypoint.sh:/couchdb-entrypoint.sh:ro
- ./obsidian-livesync/livesync.ini:/livesync.ini:ro
labels:
- diun.enable=true
# No middleware chain: CouchDB handles CORS itself, and chain-no-auth's
# secure-headers (GET/OPTIONS/PUT only) and rate limit break LiveSync.
- "traefik.enable=true"
- "traefik.http.services.livesync-service.loadbalancer.server.port=5984"
- "traefik.http.routers.livesync-secure.entrypoints=web-secure"
- "traefik.http.routers.livesync-secure.rule=Host(`livesync.${DOMAIN}`)"
- "traefik.http.routers.livesync-secure.tls.certresolver=milvert_dns"
- "traefik.http.routers.livesync-secure.tls=true"
# Mirrors the CouchDB vault to ~/repo/notes as markdown.
# No published image, so it is built from a pinned commit.
livesync-bridge:
build: https://github.com/vrtmrz/livesync-bridge.git#fc0e028d33b3a12ddee1322951375e05107edc55
image: livesync-bridge:fc0e028
container_name: livesync-bridge
restart: unless-stopped
user: "${UID}:${GID}"
depends_on:
- couchdb
networks:
- backend
environment:
- TZ=${TZ}
- LSB_CONFIG=/run/secrets/livesync_bridge_config
secrets:
- livesync_bridge_config
volumes:
- /home/simon/repo/notes:/app/data/vault
- ${DIR_LOCAL}/livesync-bridge:/deno-dir/location_data
labels:
- "traefik.enable=false"
networks:
frontend:
external: true

View File

@ -0,0 +1,9 @@
#!/bin/sh
# Reads the admin password from the Docker secret and installs the LiveSync
# settings before handing over to the image's own entrypoint.
set -e
export COUCHDB_PASSWORD="$(cat /run/secrets/couchdb_password)"
cp /livesync.ini /opt/couchdb/etc/local.d/10-livesync.ini
exec tini -- /docker-entrypoint.sh /opt/couchdb/bin/couchdb

View File

@ -0,0 +1,26 @@
; CouchDB settings required by Obsidian Self-hosted LiveSync.
; Values follow obsidian-livesync docker/scripts/couchdb-init.sh.
[couchdb]
single_node = true
max_document_size = 50000000
[chttpd]
require_valid_user = true
max_http_request_size = 4294967296
enable_cors = true
[chttpd_auth]
require_valid_user = true
authentication_redirect = /_utils/session.html
[httpd]
WWW-Authenticate = Basic realm="couchdb"
enable_cors = true
[cors]
origins = app://obsidian.md,capacitor://localhost,http://localhost
credentials = true
headers = accept, authorization, content-type, origin, referer
methods = GET, PUT, POST, HEAD, DELETE
max_age = 3600

View File

@ -0,0 +1,15 @@
#!/bin/sh
# Commits and pushes the LiveSync-mirrored notes repo. Run from cron.
set -eu
REPO=/home/simon/repo/notes
# Skip this run if the previous one is still going.
exec 9>/tmp/notes-autocommit.lock
flock -n 9 || exit 0
cd "$REPO"
git add -A
git diff --cached --quiet && exit 0
git commit -q -m "Autocommit $(date '+%Y-%m-%d %H:%M')"
git push -q origin main