CouchDB + Obsidian LiveSync로 메모 동기화 구축하기
Oracle ARM 서버에 CouchDB를 Docker로 올리고, Obsidian Self-hosted LiveSync 플러그인으로 실시간 메모 동기화를 구축하면서 만난 삽질들. CORS, max_document_size, 시스템 DB 누락까지 실전 트러블슈팅.
📚 1인 인프라 구축기 시리즈 (3편)
CouchDB + Obsidian LiveSync로 메모 동기화 구축하기
💡 Tip. 바쁜 현대인들을 위한 본문 요약
- CouchDB + Obsidian LiveSync 조합으로 외부 서비스 없이 메모 실시간 동기화를 구축할 수 있다
- Docker로 올린 뒤 시스템 DB(
_users,_replicator)를 반드시 수동 생성해야 한다 — 빠뜨리면 500 에러- Obsidian 앱은
app://obsidian.mdorigin을 쓰므로 CouchDB 자체 CORS 설정이 필수 (Nginx 중복 금지)- 기본
max_document_size는 8MB — 이미지/첨부가 있다면 50MB 이상으로 확장 필요- 두 번째 기기 연결 시 “서버 초기화” 대신 반드시 “Fetch from Remote” 사용
Obsidian을 Windows와 Mac Mini 두 대에서 쓰고 있다.
Obsidian Sync? 월 $4. iCloud 동기화? Windows에서 안 된다. Git으로 동기화? 충돌 나면 미쳐버린다.
결국 CouchDB 기반의 Self-hosted LiveSync를 선택했다. CouchDB를 내 서버에 직접 올리고, 실시간 양방향 동기화하는 방식이다.
“CouchDB 하나 올리면 끝이겠지” 했는데, 전혀 아니었다 💀
🔍 증상: 동기화가 안 되는 세 가지 상황

Obsidian에 Self-hosted LiveSync 플러그인을 설치하고 CouchDB 주소를 넣었다. “Test” 버튼을 누르는 순간 에러 3연타.
⚠️ 증상 1 — CORS 에러로 연결 자체 실패
플러그인 설정에서 CouchDB URL을 입력하고 테스트하면 바로 막힌다.
Access to fetch at 'https://obs.example.com/' from origin 'app://obsidian.md'
has been blocked by CORS policy
Obsidian 데스크톱 앱은 app://obsidian.md라는 독자적인 origin을 사용한다.
CouchDB는 기본적으로 CORS를 전면 차단한다.
Nginx에서 CORS 헤더를 추가해봤자 CouchDB 레벨에서 막으면 소용없다.
⚠️ 증상 2 — 시스템 DB 누락으로 500 에러
CORS를 해결하고 나니 이번엔 500 에러다.
{"error":"not_found","reason":"Database does not exist."}
CouchDB를 Docker로 처음 올리면 _users와 _replicator 시스템 데이터베이스가 자동으로 생성되지 않는다.
LiveSync 플러그인이 내부적으로 이 DB에 접근하려 하면서 터진다.
⚠️ 증상 3 — 큰 파일 동기화 실패
연결은 됐는데, 일부 노트가 동기화되지 않는다. Obsidian 플러그인 로그를 보면:
document_too_large: {"error":"document_too_large","reason":"..."}
CouchDB의 기본 max_document_size는 8MB다.
Obsidian 노트에 이미지를 인라인으로 넣거나 첨부 파일이 조금만 커도 이 제한에 걸린다.
LiveSync는 바이너리 파일도 CouchDB 도큐먼트로 저장하기 때문이다.
Base64 인코딩까지 고려하면 실제 용량의 약 1.33배가 도큐먼트 크기가 된다. 결국 6MB 파일이면 이미 8MB 제한을 초과한다.
🧠 원인: CouchDB 기본값은 Obsidian용이 아니다

CouchDB는 범용 NoSQL DB다. Obsidian LiveSync의 요구사항은 CouchDB 기본 설정과 여러 군데 충돌한다.
원인 1 — CORS 허용 origin 미설정
CouchDB [httpd] 섹션의 enable_cors가 기본값 false다.
Obsidian 앱은 플랫폼마다 다른 origin을 사용한다.
| 플랫폼 | Origin |
|---|---|
| 데스크톱 (Electron) | app://obsidian.md |
| 모바일 (Capacitor) | capacitor://localhost |
| 웹 뷰어 | http://localhost |
세 개 전부 등록해야 어디서든 동기화된다.
핵심: CORS는 CouchDB 공식 문서에도 명시된 필수 설정이다. Nginx 레벨이 아닌 CouchDB 자체에서 설정해야 한다.
원인 2 — 시스템 DB 수동 생성 필요
CouchDB 3.x의 Docker 이미지는 “single node setup”을 자동으로 완료하지 않는다.
/_cluster_setup을 호출하거나, _users와 _replicator를 직접 만들어야 한다.
CouchDB Docker 공식 문서에도 있는 내용이지만, Docker Compose로 한 줄 올리고 바로 쓰려는 사람은 놓치기 쉽다.
원인 3 — max_document_size 기본값이 너무 작다
LiveSync는 노트 본문뿐 아니라 첨부 파일(이미지, PDF 등)도 CouchDB 도큐먼트로 저장한다. 6MB 이상 파일은 인코딩 후 8MB 제한을 넘어서 전부 실패한다.
주의: 이미지 하나 없는 텍스트 볼트라면 기본값으로도 버틸 수 있다. 그러나 첨부 파일이 있는 순간 무조건 터진다.
🛠️ 해결: Docker Compose + CouchDB 설정 + Nginx

✅ Step 1 — Docker Compose에 CouchDB 추가
기존 스택에 CouchDB 컨테이너를 추가했다.
# docker-compose.yml (발췌)
couchdb:
image: couchdb:3.5.1
restart: unless-stopped
environment:
- COUCHDB_USER=obsidian
- COUCHDB_PASSWORD=${COUCHDB_PASSWORD}
volumes:
- couchdb-data:/opt/couchdb/data
networks:
- wp-net
핵심: ARM 서버에서도
couchdb:3.5.1공식 이미지가linux/arm64를 지원한다. 별도 빌드가 필요 없다.
✅ Step 2 — 시스템 DB 생성
컨테이너가 올라온 직후, 시스템 DB를 수동으로 생성한다.
# CouchDB 시스템 DB 초기화
curl -X PUT http://obsidian:${COUCHDB_PASSWORD}@localhost:5984/_users
curl -X PUT http://obsidian:${COUCHDB_PASSWORD}@localhost:5984/_replicator
# Obsidian 볼트용 DB 생성
curl -X PUT http://obsidian:${COUCHDB_PASSWORD}@localhost:5984/obsidian-vault
이걸 빠뜨리면 LiveSync 플러그인이 500 에러를 뱉는다. Docker entrypoint 스크립트에 넣어두면 재배포할 때도 안전하게 넘어간다.
✅ Step 3 — CORS 설정
CouchDB REST API로 직접 설정하는 게 가장 확실하다.
# CORS 활성화
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/httpd/enable_cors \
-d '"true"'
# 허용 origin 등록 (3개 전부 필요)
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/cors/origins \
-d '"app://obsidian.md,capacitor://localhost,http://localhost"'
# 허용 메서드
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/cors/methods \
-d '"GET, PUT, POST, HEAD, DELETE"'
# 허용 헤더
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/cors/headers \
-d '"accept, authorization, content-type, origin, referer"'
# 크리덴셜 허용
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/cors/credentials \
-d '"true"'
팁: Nginx 레벨에서 CORS 헤더를 따로 추가하면 안 된다.
Access-Control-Allow-Origin헤더가 두 번 나와서 오히려 브라우저가 거부한다. CouchDB 자체 설정으로만 처리하는 게 맞다.
✅ Step 4 — max_document_size 확장
curl -X PUT http://obsidian:${PW}@localhost:5984/_node/_local/_config/couchdb/max_document_size \
-d '"50000000"'
50MB로 설정했다. Obsidian 볼트에 50MB짜리 파일을 넣을 일은 거의 없지만, 여유 있게 잡아두는 게 낫다. 이 설정을 빠뜨리면 6MB 이상 이미지가 하나씩 조용히 누락된다 — 에러 메시지도 모호해서 원인 찾는 데 시간을 날린다.
✅ Step 5 — Nginx 리버스 프록시
server {
listen 443 ssl;
server_name obs.example.com;
ssl_certificate /etc/nginx/certs/origin.pem;
ssl_certificate_key /etc/nginx/certs/origin.key;
location / {
proxy_pass http://couchdb:5984;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS 헤더는 CouchDB가 직접 내보냄 — 여기서 add_header 금지
}
}
주의:
add_header Access-Control-Allow-Origin을 Nginx에 넣지 않는다. CouchDB가 직접 CORS 헤더를 붙이기 때문에 Nginx에서 중복 추가하면 브라우저가 즉시 거부한다.
Cloudflare에 A 레코드를 추가하고 SSL 모드는 “Full (Strict)“으로 설정했다. 이미 와일드카드 Origin Certificate가 있으면 인증서 추가 발급은 불필요하다.
팁: Cloudflare SSL 설정이 궁금하다면 이전 글 Cloudflare Full Strict SSL + Nginx 리버스 프록시 삽질 총정리에서 자세히 다뤘다.
✅ Step 6 — Obsidian 플러그인 설정
Self-hosted LiveSync 플러그인(v0.25.50)을 설치하고 아래 값을 입력한다.
| 설정 | 값 |
|---|---|
| CouchDB URL | https://obs.example.com |
| Username | obsidian |
| Password | (Keychain에서 조회) |
| Database name | obsidian-vault |
| 동기화 모드 | LiveSync (실시간) |
첫 번째 기기(Windows)에서 “서버 초기화(Rebuild everything)“를 실행해서 볼트를 서버에 업로드한다. 두 번째 기기(Mac Mini)에서는 “Fetch from Remote”로 볼트를 가져온다.
핵심: 두 번째 기기에서 절대로 “서버 초기화”를 누르면 안 된다. 서버에 올라간 데이터를 전부 날려버린다.
🛡️ 예방: 재발 방지 체크리스트
CouchDB를 Docker로 올릴 때마다 확인할 항목들이다.
배포 전 체크리스트
-
_users,_replicator시스템 DB 생성 확인 - CORS
enable_cors = true확인 - CORS
origins에app://obsidian.md포함 확인 -
max_document_size가 50MB 이상인지 확인 - Nginx에서 CORS 헤더를 추가하지 않았는지 확인 (중복 방지)
- Cloudflare SSL 모드: Full (Strict) 확인
- CouchDB 비밀번호가 Keychain에 저장되었는지 확인
동기화 장애 시 디버그 순서
# 1. CouchDB 컨테이너 상태 확인
docker compose ps couchdb
docker compose logs couchdb --tail=20
# 2. CouchDB 직접 접근 테스트 (컨테이너 내부)
docker compose exec couchdb curl -s http://localhost:5984/
# 3. Nginx 프록시 테스트 (외부)
curl -s https://obs.example.com/
# 4. CORS 설정 확인
curl -s http://obsidian:PW@localhost:5984/_node/_local/_config/cors/origins
# 5. DB 존재 여부 확인
curl -s http://obsidian:PW@localhost:5984/_all_dbs
팁: Obsidian 플러그인의 “Test” 버튼이 실패하면, 먼저
curl로 CouchDB에 직접 접근해봐야 한다. 플러그인 에러 메시지는 근본 원인을 숨기는 경우가 많다.
📋 정리

| 상황 | 안티패턴 | 권장 패턴 |
|---|---|---|
| CouchDB Docker 초기화 | docker compose up만 하고 끝 | 시스템 DB(_users, _replicator) 수동 생성 |
| CORS 설정 | Nginx에서 CORS 헤더 추가 | CouchDB 자체 CORS 설정 사용 (중복 헤더 방지) |
| 큰 파일 동기화 실패 | 기본 8MB 제한 방치 | max_document_size 50MB로 확장 |
| 두 번째 기기 연결 | ”서버 초기화” 실행 | ”Fetch from Remote”로 기존 데이터 수신 |
| 비밀번호 관리 | .env에만 저장 | Keychain + secrets-registry 이중 기록 |
CouchDB + Obsidian LiveSync는 한번 세팅하면 정말 편하다. iCloud/Google Drive 동기화의 충돌 지옥에서 벗어나서, 실시간으로 양방향 동기화가 된다.
초기 설정에서 빠뜨리기 쉬운 항목이 딱 세 개다. 시스템 DB, CORS, document size — 하나라도 놓치면 플러그인이 모호한 에러만 보여준다.
체크리스트를 한 번만 따라가면, 그다음부턴 신경 쓸 일이 없다 ✨
함께 읽으면 좋은 글
- Oracle ARM + Docker로 WordPress 4사이트 운영하기 — 이 글의 서버 인프라 기반
- Cloudflare Full Strict SSL + Nginx 리버스 프록시 삽질 총정리 — SSL/프록시 설정 상세
📚 1인 인프라 구축기 시리즈 (3편)
- 1. Oracle ARM + Docker로 WordPress 4사이트 운영하기
- 2. Cloudflare Full Strict SSL + Nginx 리버스 프록시 삽질 총정리
- 3. CouchDB + Obsidian LiveSync로 메모 동기화 구축하기