infra/obsidian-sync/docker-compose.yml
Mannu 88f8876829 fix(obsidian-sync): run CouchDB as uid 5984 to bypass entrypoint chown
The CouchDB 3.3 entrypoint runs as root and executes:
  find /opt/couchdb \! -user couchdb -exec chown couchdb {} +
before writing a single log line. The :ro bind-mounted local.ini is
owned by root on the host, so chown fails with EROFS — set -e exits
immediately with code 1 and zero output (381ms crash, empty logs).

Running as user 5984:5984 skips the root block in the entrypoint
entirely, going straight to admin setup and CouchDB launch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 09:38:15 +05:30

70 lines
3.1 KiB
YAML

# =============================================================================
# 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 is CouchDB's lightweight liveness endpoint (no auth needed).
test: ["CMD", "curl", "-f", "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: