feat(couchdb): deploy CouchDB for Obsidian sync (communicore)
This commit is contained in:
280
cluster/applications/couchdb/README.md
Normal file
280
cluster/applications/couchdb/README.md
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
# CouchDB Deployment
|
||||||
|
|
||||||
|
**Application Name:** couchdb
|
||||||
|
**Thematic Name:** communicore (DNS only)
|
||||||
|
**Purpose:** Obsidian sync backend for mk-labs
|
||||||
|
**Deployment Method:** Helm chart via ArgoCD
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This deployment provides a CouchDB instance for Obsidian sync across multiple devices in the mk-labs homelab. It replaces the previous NFS-based shared vault with a proper sync solution.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Helm Chart:** Apache CouchDB official chart (v4.6.3, CouchDB 3.5.1)
|
||||||
|
- **Cluster:** Single-node deployment (suitable for homelab)
|
||||||
|
- **Storage:** 20Gi persistent volume using nfs-emporium StorageClass
|
||||||
|
- **Secrets:** ExternalSecrets Operator pulling from 1Password
|
||||||
|
- **TLS:** cert-manager with letsencrypt-staging (switch to prod after validation)
|
||||||
|
- **Ingress:** nginx-ingress with external-dns
|
||||||
|
|
||||||
|
## DNS Configuration
|
||||||
|
|
||||||
|
- **Primary:** communicore.mk-labs.cloud (thematic name)
|
||||||
|
- Managed automatically by external-dns via ingress annotations
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### 1Password Vault Setup
|
||||||
|
|
||||||
|
Create a new item in 1Password (mk-labs vault):
|
||||||
|
|
||||||
|
```
|
||||||
|
Item Name: couchdb
|
||||||
|
Type: Login
|
||||||
|
|
||||||
|
Fields:
|
||||||
|
admin-password: (generate strong password, 32 characters)
|
||||||
|
cookie-auth-secret: (generate random string, 32 characters)
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate the secrets:
|
||||||
|
```bash
|
||||||
|
# Admin password
|
||||||
|
openssl rand -base64 32
|
||||||
|
|
||||||
|
# Cookie auth secret
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Store both values in the 1Password item before deploying.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Initial Deployment
|
||||||
|
|
||||||
|
The deployment is managed entirely through GitOps:
|
||||||
|
|
||||||
|
1. Commit and push the manifests to the homelab repository
|
||||||
|
2. ArgoCD will automatically detect and sync the application
|
||||||
|
3. Monitor the deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Watch ArgoCD sync status
|
||||||
|
kubectl get application -n argocd couchdb -w
|
||||||
|
|
||||||
|
# Watch pod creation
|
||||||
|
kubectl get pods -n couchdb -w
|
||||||
|
|
||||||
|
# Check deployment status
|
||||||
|
kubectl get all -n couchdb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verification Steps
|
||||||
|
|
||||||
|
1. **Check ExternalSecret sync:**
|
||||||
|
```bash
|
||||||
|
kubectl get externalsecret -n couchdb
|
||||||
|
kubectl get secret -n couchdb couchdb-admin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Verify pod health:**
|
||||||
|
```bash
|
||||||
|
kubectl get pods -n couchdb
|
||||||
|
kubectl logs -n couchdb -l app=couchdb
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Test internal access:**
|
||||||
|
```bash
|
||||||
|
kubectl port-forward -n couchdb svc/couchdb-svc-couchdb 5984:5984
|
||||||
|
curl -u admin:PASSWORD http://localhost:5984/
|
||||||
|
# Should return: {"couchdb":"Welcome","version":"3.5.1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Check certificate:**
|
||||||
|
```bash
|
||||||
|
kubectl get certificate -n couchdb
|
||||||
|
kubectl describe certificate -n couchdb couchdb-tls
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Test external access:**
|
||||||
|
```bash
|
||||||
|
# Once DNS propagates (may take 2-5 minutes)
|
||||||
|
curl -k https://communicore.mk-labs.cloud/
|
||||||
|
# Should show CouchDB welcome message
|
||||||
|
```
|
||||||
|
|
||||||
|
### Switch to Production Certificates
|
||||||
|
|
||||||
|
After successful validation (24-48 hours stable), switch to production certificates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/git/homelab/cluster/applications/couchdb
|
||||||
|
|
||||||
|
# Update values.yaml
|
||||||
|
sed -i 's/letsencrypt-staging/letsencrypt-prod/g' values.yaml
|
||||||
|
|
||||||
|
# Commit and push
|
||||||
|
git add values.yaml
|
||||||
|
git commit -m "feat(couchdb): switch to production TLS certificates"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Delete old staging certificate and secret to trigger reissuance
|
||||||
|
kubectl delete certificate -n couchdb couchdb-tls
|
||||||
|
kubectl delete secret -n couchdb couchdb-tls
|
||||||
|
|
||||||
|
# Verify new certificate
|
||||||
|
kubectl get certificate -n couchdb
|
||||||
|
```
|
||||||
|
|
||||||
|
## Obsidian Configuration
|
||||||
|
|
||||||
|
### Plugin Installation
|
||||||
|
|
||||||
|
On each device (workstation, desktop, carousel-of-progress):
|
||||||
|
|
||||||
|
1. Install "Self-hosted LiveSync" plugin in Obsidian
|
||||||
|
2. Enable the plugin
|
||||||
|
3. Configure as follows:
|
||||||
|
|
||||||
|
### Connection Settings
|
||||||
|
|
||||||
|
```
|
||||||
|
Remote Database URL: https://communicore.mk-labs.cloud
|
||||||
|
Remote Database: obsidian-sync
|
||||||
|
Username: admin
|
||||||
|
Password: (from 1Password couchdb item)
|
||||||
|
End-to-end Encryption: ✓ Enabled
|
||||||
|
Passphrase: (create a strong passphrase, store in 1Password)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Initial Sync (IMPORTANT)
|
||||||
|
|
||||||
|
**ONLY ONE DEVICE** should perform the initial upload:
|
||||||
|
|
||||||
|
1. **On Workstation (primary device):**
|
||||||
|
- Open Obsidian settings → Self-hosted LiveSync
|
||||||
|
- Click "Rebuild Everything"
|
||||||
|
- Wait for upload to complete (may take several minutes)
|
||||||
|
|
||||||
|
2. **On Other Devices (desktop, carousel):**
|
||||||
|
- Open Obsidian settings → Self-hosted LiveSync
|
||||||
|
- Click "Fetch from Remote"
|
||||||
|
- Wait for download to complete
|
||||||
|
|
||||||
|
### Vault Locations
|
||||||
|
|
||||||
|
As documented in communicore-decisions.md:
|
||||||
|
|
||||||
|
- **Workstation:** ~/friday
|
||||||
|
- **Desktop:** ~/friday
|
||||||
|
- **Carousel-of-progress:** (to be determined by JARVIS)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Pod Not Starting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check events
|
||||||
|
kubectl describe pod -n couchdb <pod-name>
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
kubectl logs -n couchdb <pod-name>
|
||||||
|
|
||||||
|
# Common issues:
|
||||||
|
# - Secret not synced: check ExternalSecret status
|
||||||
|
# - PVC pending: verify storage class and NFS CSI driver
|
||||||
|
```
|
||||||
|
|
||||||
|
### Certificate Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check certificate status
|
||||||
|
kubectl get certificate -n couchdb
|
||||||
|
kubectl describe certificate -n couchdb couchdb-tls
|
||||||
|
|
||||||
|
# Check cert-manager logs
|
||||||
|
kubectl logs -n cert-manager -l app=cert-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
### DNS Not Resolving
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check external-dns logs
|
||||||
|
kubectl logs -n external-dns -l app.kubernetes.io/name=external-dns
|
||||||
|
|
||||||
|
# Verify ingress annotation
|
||||||
|
kubectl get ingress -n couchdb -o yaml | grep external-dns
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Refused in Obsidian
|
||||||
|
|
||||||
|
1. Verify external access works from command line first
|
||||||
|
2. Check that the database exists:
|
||||||
|
```bash
|
||||||
|
curl -u admin:PASSWORD https://communicore.mk-labs.cloud/_all_dbs
|
||||||
|
```
|
||||||
|
3. If obsidian-sync database doesn't exist, create it:
|
||||||
|
```bash
|
||||||
|
curl -u admin:PASSWORD -X PUT https://communicore.mk-labs.cloud/obsidian-sync
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
CouchDB data is stored on nfs-emporium persistent volume. The NFS backend (Synology at 10.1.71.9) should handle snapshots.
|
||||||
|
|
||||||
|
Additional backup options:
|
||||||
|
- CouchDB replication to backup instance
|
||||||
|
- Scheduled exports to S3/Minio
|
||||||
|
- Git backup of vault using Obsidian Git plugin
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
If deployment fails or causes issues:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Delete the ArgoCD application
|
||||||
|
kubectl delete application -n argocd couchdb
|
||||||
|
|
||||||
|
# Delete the namespace (will cascade delete all resources)
|
||||||
|
kubectl delete namespace couchdb
|
||||||
|
|
||||||
|
# Remove from git
|
||||||
|
cd ~/git/homelab
|
||||||
|
git rm -rf cluster/applications/couchdb
|
||||||
|
git commit -m "rollback: remove couchdb deployment"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Upgrade CouchDB Version
|
||||||
|
|
||||||
|
1. Update chart version in application.yaml
|
||||||
|
2. Update image tag in values.yaml
|
||||||
|
3. Test in staging first
|
||||||
|
4. Commit and push to trigger ArgoCD sync
|
||||||
|
|
||||||
|
### Rotate Admin Password
|
||||||
|
|
||||||
|
1. Update password in 1Password
|
||||||
|
2. ExternalSecret will automatically sync within 1h (or trigger manually)
|
||||||
|
3. Update Obsidian plugin configuration on all devices
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [CouchDB Documentation](https://docs.couchdb.org/)
|
||||||
|
- [Helm Chart Repository](https://github.com/apache/couchdb-helm)
|
||||||
|
- [Self-hosted LiveSync Plugin](https://github.com/vrtmrz/obsidian-livesync)
|
||||||
|
- [communicore-decisions.md](file:///mnt/mk-labs-pka/inbox/team/communicore-decisions.md)
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- **Created:** 2026-06-05
|
||||||
|
- **Status:** Ready for deployment
|
||||||
|
- **Next Steps:**
|
||||||
|
1. Create 1Password item with credentials
|
||||||
|
2. Commit and push to homelab repo
|
||||||
|
3. Monitor ArgoCD sync
|
||||||
|
4. Validate external access
|
||||||
|
5. Configure Obsidian on all devices
|
||||||
45
cluster/applications/couchdb/application.yaml
Normal file
45
cluster/applications/couchdb/application.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: couchdb
|
||||||
|
namespace: argocd
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: couchdb
|
||||||
|
app.kubernetes.io/part-of: mk-labs
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "20" # Wave 20 — applications tier
|
||||||
|
spec:
|
||||||
|
project: default
|
||||||
|
sources:
|
||||||
|
# Helm chart from official Apache CouchDB repository
|
||||||
|
- repoURL: https://apache.github.io/couchdb-helm
|
||||||
|
chart: couchdb
|
||||||
|
targetRevision: 4.6.3
|
||||||
|
helm:
|
||||||
|
valueFiles:
|
||||||
|
- $values/cluster/applications/couchdb/values.yaml
|
||||||
|
# Values and manifests from homelab repo
|
||||||
|
- repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||||
|
targetRevision: HEAD
|
||||||
|
ref: values
|
||||||
|
# Additional manifests (namespace, externalsecret)
|
||||||
|
- repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: cluster/applications/couchdb
|
||||||
|
directory:
|
||||||
|
recurse: false
|
||||||
|
include: '{namespace.yaml,externalsecret.yaml}'
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: couchdb
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- ApplyOutOfSyncOnly=true
|
||||||
|
syncWaves:
|
||||||
|
- order: 0 # Namespace first
|
||||||
|
- order: 1 # ExternalSecret second
|
||||||
|
- order: 2 # Helm chart last
|
||||||
31
cluster/applications/couchdb/externalsecret.yaml
Normal file
31
cluster/applications/couchdb/externalsecret.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: couchdb-credentials
|
||||||
|
namespace: couchdb
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: couchdb
|
||||||
|
app.kubernetes.io/part-of: mk-labs
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
name: onepassword-connect
|
||||||
|
target:
|
||||||
|
name: couchdb-admin
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
engineVersion: v2
|
||||||
|
data:
|
||||||
|
adminUsername: "admin"
|
||||||
|
adminPassword: "{{ .adminPassword }}"
|
||||||
|
cookieAuthSecret: "{{ .cookieAuthSecret }}"
|
||||||
|
data:
|
||||||
|
- secretKey: adminPassword
|
||||||
|
remoteRef:
|
||||||
|
key: couchdb
|
||||||
|
property: admin-password
|
||||||
|
- secretKey: cookieAuthSecret
|
||||||
|
remoteRef:
|
||||||
|
key: couchdb
|
||||||
|
property: cookie-auth-secret
|
||||||
7
cluster/applications/couchdb/namespace.yaml
Normal file
7
cluster/applications/couchdb/namespace.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: couchdb
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: couchdb
|
||||||
|
app.kubernetes.io/part-of: mk-labs
|
||||||
89
cluster/applications/couchdb/values.yaml
Normal file
89
cluster/applications/couchdb/values.yaml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# CouchDB Helm Chart Values
|
||||||
|
# Application: couchdb
|
||||||
|
# Theme: communicore (DNS only)
|
||||||
|
# Purpose: Obsidian sync backend for mk-labs
|
||||||
|
|
||||||
|
# Cluster configuration
|
||||||
|
clusterSize: 1 # Single node for homelab
|
||||||
|
|
||||||
|
# Admin credentials managed via ExternalSecret
|
||||||
|
# See externalsecret.yaml for 1Password integration
|
||||||
|
createAdminSecret: false
|
||||||
|
extraSecretName: "couchdb-admin"
|
||||||
|
adminUsernameKey: "adminUsername"
|
||||||
|
adminPasswordKey: "adminPassword"
|
||||||
|
cookieAuthSecretKey: "cookieAuthSecret"
|
||||||
|
|
||||||
|
# Auto-setup cluster after installation
|
||||||
|
autoSetup:
|
||||||
|
enabled: true
|
||||||
|
image:
|
||||||
|
repository: curlimages/curl
|
||||||
|
tag: "8.11.1" # Pinned version
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
defaultDatabases:
|
||||||
|
- _global_changes
|
||||||
|
|
||||||
|
# Network policy for pod communication
|
||||||
|
networkPolicy:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Service account
|
||||||
|
serviceAccount:
|
||||||
|
enabled: true
|
||||||
|
create: true
|
||||||
|
|
||||||
|
# Persistent storage
|
||||||
|
persistentVolume:
|
||||||
|
enabled: true
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
size: 20Gi
|
||||||
|
storageClass: "nfs-emporium"
|
||||||
|
|
||||||
|
# CouchDB image - pinned version
|
||||||
|
image:
|
||||||
|
repository: couchdb
|
||||||
|
tag: "3.5.1"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# Service configuration
|
||||||
|
service:
|
||||||
|
enabled: true
|
||||||
|
type: ClusterIP
|
||||||
|
port: 5984
|
||||||
|
|
||||||
|
# Ingress configuration
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-staging" # Start with staging
|
||||||
|
external-dns.alpha.kubernetes.io/hostname: "communicore.mk-labs.cloud"
|
||||||
|
hosts:
|
||||||
|
- communicore.mk-labs.cloud
|
||||||
|
tls:
|
||||||
|
- secretName: couchdb-tls
|
||||||
|
hosts:
|
||||||
|
- communicore.mk-labs.cloud
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
# CouchDB configuration
|
||||||
|
couchdbConfig:
|
||||||
|
couchdb:
|
||||||
|
uuid: "decafbaddecafbaddecafbaddecafbad" # Will be regenerated on first run
|
||||||
|
chttpd:
|
||||||
|
bind_address: "0.0.0.0"
|
||||||
|
port: 5984
|
||||||
|
require_valid_user: true
|
||||||
|
httpd:
|
||||||
|
bind_address: "0.0.0.0"
|
||||||
|
port: 5986
|
||||||
Reference in New Issue
Block a user