# ============================================================================= # obsidian-sync : CouchDB hub for Obsidian Self-hosted LiveSync # Deploy as a Dokploy "Compose" app. Image is pulled (no build). # Public endpoint: https://couchdb.manohargupta.com (CouchDB-native auth only) # ============================================================================= services: couchdb: # Pin a known-good 3.x line. LiveSync needs >=3.2; 3.3 is well-tested. image: couchdb:3.3 container_name: obsidian-couchdb restart: unless-stopped # Run as the couchdb user (uid 5984) from the start. This skips the # entrypoint's "find /opt/couchdb \! -user couchdb -exec chown {}" step, # which fails with EROFS when the :ro bind-mounted ini file is encountered — # causing an immediate silent exit before any log is written. user: "5984:5984" # Admin credentials come from Dokploy's Environment tab (NOT hard-coded here, # so they never land in git). Set COUCHDB_USER / COUCHDB_PASSWORD in the UI. environment: - COUCHDB_USER=${COUCHDB_USER} - COUCHDB_PASSWORD=${COUCHDB_PASSWORD} volumes: # Persistent database files (named volume, survives redeploys). - couchdb-data:/opt/couchdb/data # LiveSync-tuned config. Mounted into the *.d override dir so it layers on # top of CouchDB's defaults without us editing the base file. - ./couchdb/local.ini:/opt/couchdb/etc/local.d/10-livesync.ini:ro # mem_limit intentionally omitted: on a swap-heavy box the cgroup OOM killer # fires before the Erlang VM writes its first log line (kernel kills it, so # docker inspect shows OOMKilled:false — misleading). Re-add once swap < 50%. healthcheck: # _up requires auth because require_valid_user = true covers all endpoints. # CMD-SHELL lets the container shell expand $COUCHDB_USER/$COUCHDB_PASSWORD # (the $$ in YAML becomes $ after Compose substitution, then the shell runs it). test: ["CMD-SHELL", "curl -f -u $$COUCHDB_USER:$$COUCHDB_PASSWORD http://localhost:5984/_up"] interval: 30s timeout: 10s retries: 5 start_period: 30s networks: - dokploy-network labels: - traefik.enable=true # --- HTTP router on :80 (serves the ACME challenge; matches your Dokploy # convention seen on position-tracker). CouchDB 401s unauth requests # itself, so no auth middleware needed here. --- - traefik.http.routers.obsidian-couchdb-http.rule=Host(`couchdb.manohargupta.com`) - traefik.http.routers.obsidian-couchdb-http.entrypoints=web # --- HTTPS router on :443 --- - traefik.http.routers.obsidian-couchdb.rule=Host(`couchdb.manohargupta.com`) - traefik.http.routers.obsidian-couchdb.entrypoints=websecure - traefik.http.routers.obsidian-couchdb.tls=true - traefik.http.routers.obsidian-couchdb.tls.certresolver=letsencrypt # --- Service: CouchDB listens on 5984 --- - traefik.http.services.obsidian-couchdb.loadbalancer.server.port=5984 # Tell Traefik which network to reach the container on (overlay). - traefik.docker.network=dokploy-network # NOTE: deliberately NO basicauth middleware here. CouchDB does its own auth. networks: dokploy-network: external: true volumes: couchdb-data: