Obsidian-livesync Setup

Want to use Obsidian Live Sync without installing Deno? Learn how to set up CouchDB with Docker Compose and containerize the Deno configuration script for a clean, hassle-free sync server setup.

Obsidian-livesync Setup

I have been a longtime Joplin user, since it provides it own official sync server, however I wanted to try out Obsidian, but setting up and figuring out a sync server was a journey of its own.

I finally settled on the https://github.com/vrtmrz/obsidian-livesync since it was relatively easy to setup, but one thing I did not want to install was yet another javascript compiler, deno hence I want to show you how I went about dockerizing the setup scriptt

Setup

First we have setup and configure couchdb, for this I used docker compose and followed the official docs.

Couchdb

services:
  couchdb:
    image: couchdb:3.5.2
    user: 5984:5984
    environment:
      - COUCHDB_USER=user  #Please change as you like.
      - COUCHDB_PASSWORD=user #Please change as you like.
    volumes:
      - ./couchdb-data:/opt/couchdb/data
      - ./couchdb-etc:/opt/couchdb/etc/local.d
    ports:
      - 5984:5984
    restart: always
💡
Ensure to change the owners to 5984:5984 for the two directories:
sudo chown -R 5984:5984 /opt/docker/obsidian/Couchdb

couchdb configuration script

Once this is up and running we can run the configuration script. Since I did not want to export the variable we can prepend the bash script with the necessary variable

curl -s https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/main/utils/couchdb/couchdb-init.sh | hostname=localhost:5984 username=user password=user bash

Dockerizing the deno script

Since I did not want to install another javascript compiler, I figured to dockerize the client setup so it easy to run anywhere.

Dockerfile

ARG DENO_VERSION=2.9.3
FROM denoland/deno:alpine-${DENO_VERSION}

ARG LIVESYNC_REF=main
ENV SETUP_SCRIPT_URL="https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/${LIVESYNC_REF}/utils/flyio/generate_setupuri.ts"

USER root
WORKDIR /app

# Download and cache the upstream TypeScript script and its pinned npm dependency
# during the image build. The container can then run with --cached-only.
RUN deno cache "${SETUP_SCRIPT_URL}" \
    && chmod -R a+rX /deno-dir

COPY --chown=deno:deno generate-setup-uri.sh /app/generate-setup-uri.sh
RUN chmod 0755 /app/generate-setup-uri.sh

USER deno
ENTRYPOINT ["/app/generate-setup-uri.sh"]

generate-setup-uri.sh

#!/bin/sh
set -eu

error() {
    printf 'ERROR: %s\n' "$*" >&2
    exit 1
}

for variable in \
    LIVESYNC_HOSTNAME \
    LIVESYNC_DATABASE \
    LIVESYNC_E2EE_PASSPHRASE \
    COUCHDB_USER \
    COUCHDB_PASSWORD
do
    eval "value=\${${variable}:-}"
    [ -n "$value" ] || error "$variable is missing or empty"
done

case "$LIVESYNC_HOSTNAME" in
    http://*|https://*) ;;
    *) error "LIVESYNC_HOSTNAME must begin with http:// or https://" ;;
esac

# CouchDB database names must start with a lowercase letter. Keeping this
# validation here also prevents the LiveSync 'No capitals allowed in URL' error.
printf '%s' "$LIVESYNC_DATABASE" | grep -Eq '^[a-z][a-z0-9_$()+/-]*$' \
    || error "LIVESYNC_DATABASE must start with a lowercase letter and contain only lowercase letters, digits, _, $, (, ), +, -, or /"

# Map the Docker-friendly variable names to the lowercase names expected by
# the original LiveSync Deno script.
export hostname="${LIVESYNC_HOSTNAME%/}"
export database="$LIVESYNC_DATABASE"
export passphrase="$LIVESYNC_E2EE_PASSPHRASE"
export username="$COUCHDB_USER"
export password="$COUCHDB_PASSWORD"

# Important: an empty uri_passphrase must be UNSET. Passing an empty variable
# would make the upstream script use an empty Setup-URI passphrase rather than
# generate a friendly random one.
if [ -n "${LIVESYNC_URI_PASSPHRASE:-}" ]; then
    export uri_passphrase="$LIVESYNC_URI_PASSPHRASE"
else
    unset uri_passphrase 2>/dev/null || true
fi

exec deno run \
    --cached-only \
    --allow-env=hostname,username,password,database,passphrase,uri_passphrase \
    "$SETUP_SCRIPT_URL"

.env

# Public CouchDB endpoint, normally your HTTPS reverse-proxy URL.
LIVESYNC_HOSTNAME=https://couchdb.internal.example.com

# Must be lowercase. Example: obsidiannotes
LIVESYNC_DATABASE=obsidiannotes

# Vault end-to-end encryption passphrase.
# Single quotes keep $, #, spaces, and other special characters literal.
LIVESYNC_E2EE_PASSPHRASE='replace-with-a-long-unique-e2ee-passphrase'

# CouchDB credentials.
COUCHDB_USER='user'
COUCHDB_PASSWORD='user'

# Optional and DIFFERENT from LIVESYNC_E2EE_PASSPHRASE.
# Leave commented or empty to let the upstream script generate one.
# LIVESYNC_URI_PASSPHRASE='replace-with-a-separate-setup-uri-passphrase'

You can then build this image docker build -t client-setup . and then run it docker run --rm -it --env-file .env client-setup