Q19 resolved: storage convention is plain host bind-mount, not SMB volume

The Karakeep compose user shared confirms every container in LXC 104
uses host bind-mounts of /mnt/pve/unas/services/<svc>/<vol> directly.
No driver_opts, no CIFS, no per-volume credentials. The earlier
SMB-as-docker-volume proposal in this repo was over-fitting to the
Nextcloud-specific NFS issue and is withdrawn.

The Nextcloud failure was Nextcloud-specific: its setup tooling chowns
the data dir to www-data, which fails against root_squash exports.
Normal containers don't trigger that path, so plain bind-mounts work
fine.

Changes:

- docs/09 §Step 3: compose block dropped from ~30 lines (volume,
  driver_opts, CIFS device, credentials env) to a one-line bind-mount
  + env_file. Added a pre-deploy `mkdir -p` for the UNAS service +
  backup paths. Explanatory note retained for future agents.
- docs/12 #27 marked withdrawn pointing to #31. #31 rewritten with the
  bind-mount pattern (was SMB-default). #36 (UNAS audit) now looks for
  `- /var/lib/docker/...` or anonymous volumes as the non-compliant
  signal.
- CLAUDE.md storage block: bind-mount is the standard, SMB is
  documented escape-hatch only.
- docs/11: Q19 moved into Resolved with the new framing. Q12 + Q20
  grouped under a new "## Backups" section header (the now-empty
  "Verification needed" section removed).
- docs/13 Task 1 struck through with done-marker pointing to docs/09.

Net: Phase-3.1 deployment YAML is now actually deployable as written.
This commit is contained in:
Claude
2026-05-05 05:01:48 +00:00
parent dfd9aa7d2a
commit a8446f8371
5 changed files with 38 additions and 59 deletions
+8 -8
View File
@@ -71,17 +71,17 @@ Confirmed allocation: 16 CPU, 31.25 GiB RAM (7.86 GiB used / 25%), 8 GiB swap (i
lxc.idmap: g 104 104 1 # render group on host
```
Then inside the container: `usermod -aG video,render <docker-user>` and run TEI with `--device cuda` replaced by the OpenVINO build (`text-embeddings-inference:cpu-1.5-openvino`). Not needed for Phase-3.1's volume but a clean upgrade path.
31. **Match the homelab-wide UNAS storage convention.** Every existing docker stack on LXC 104 already binds its persistent data into `services/<svc>/` on UNAS via SMB (immich, karakeep, nextcloud, ntfy, open-webui, paperless-ai, pocketid, shelfmark, siyuan, stremio, traccar, vaultwarden, gluetun all follow this). Nexa **must** follow the same pattern at `services/nexa/`. Do not invent a parallel layout. Targets:
- `services/nexa/qdrant/` — Qdrant data dir (move before the visual collection lands).
- `services/nexa/tei-cache/` — embedding model cache (keeps multi-GB models off the boot disk).
- `services/nexa/graphdb/` — Phase-3.4 RDF store.
- `backup/nexa/snapshots/qdrant/<date>/` — daily Qdrant snapshots before rsync to S3 (Phase 3.3 — Q12 open). Mirrors the `backup/<svc>/` pattern used by Home Assistant / Immich.
31. **Match the homelab-wide UNAS storage convention — host bind-mount of `/mnt/pve/unas/services/<svc>/<vol>`.** Verified via the running Karakeep stack (Q19): every container in LXC 104 binds its persistent data into `/mnt/pve/unas/services/<svc>/...` directly, no `driver_opts`, no CIFS, no credentials in compose. Nexa follows the same pattern. Targets:
- `/mnt/pve/unas/services/nexa/qdrant/` — Qdrant data dir.
- `/mnt/pve/unas/services/nexa/tei-cache/` — embedding model cache (keeps multi-GB models off the boot disk).
- `/mnt/pve/unas/services/nexa/graphdb/` — Phase-3.4 RDF store.
- `/mnt/pve/unas/backup/nexa/snapshots/qdrant/<date>/` — daily Qdrant snapshots before rsync to S3 (Phase 3.3 — Q12 open). Mirrors the `backup/<svc>/` pattern used by Home Assistant / Immich.
- **n8n executions retention**: `EXECUTIONS_DATA_PRUNE=true`, `EXECUTIONS_DATA_MAX_AGE=168`.
- **Monthly `docker image prune --all --filter "until=720h"`** to clear dangling layers.
Why SMB and not the host's NFS mount: the user already lived through the unprivileged-LXC + NFS + per-container-write trap with Nextcloud. SMB mounted directly as a docker volume (driver `local`, type `cifs`) with explicit `username=`/`uid=`/`gid=` bypasses LXC uid-mapping entirely. **Pre-deploy step:** read the volume block from an existing stack (Karakeep is small and clean) and copy it verbatim — this guarantees Nexa uses the same SMB share name, credential pattern, uid/gid as the rest of the homelab. Tracked as [Q19](./11-open-questions.md#q19).
Pre-deploy step: `mkdir -p /mnt/pve/unas/services/nexa/{qdrant,tei-cache,graphdb}` and `mkdir -p /mnt/pve/unas/backup/nexa/snapshots/{qdrant,graphdb}` on the docker LXC. Then the compose volumes block is just `- /mnt/pve/unas/services/nexa/<vol>:/<container-path>`. Concrete example in [09-deployment §Step 3](./09-deployment.md#step-3--self-hosted-embeddings-tei).
Credentials (`UNAS_USER` / `UNAS_PASS`) go in Vaultwarden ([12/#11](#11)), injected at deploy. The host-side `/mnt/pve/unas` NFS mount stays useful for admin tasks (rsync, manual backups) — not for per-container write paths.
Secrets go in the stack's `.env` next to the compose (Karakeep convention: `env_file: .env`). Vaultwarden is the long-term source-of-truth for those secrets ([12/#11](#11)) but isn't required day-one. The earlier proposal of mounting SMB as a docker volume is **withdrawn** ([12/#27](#27)) — it was an over-fitting to the Nextcloud-specific NFS issue.
32. **The LXC has its own 8 GiB swap.** Combined with the host's 31 GiB, that's a lot of swap for guests that should never page. Drop the LXC swap allocation to 12 GiB (`pct set 104 -swap 2048`) — frees disk on the LVM-thin pool and forces issues to surface earlier rather than silently swap.
33. **Stacks are managed in Arcane; Dockge is stale.** The LXC was originally provisioned with the Dockge helper-script template, but the user moved on to **Arcane** as the day-to-day docker manager. Deployment of the Nexa stack therefore goes through Arcane, not Dockge. Cleanup task: tar `services/dockge/` (if it exists) into `backup/dockge/`, retire the Dockge container, drop the stale data dir. **Dozzle** stays — it's the log viewer, not a manager, so it isn't redundant with Arcane.
34. **Untriaged local mail on the LXC.** Console shows `You have new mail.` at login — the system mail spool on `/var/mail/root` has unread messages, almost always cron job failures. `mailx` or `mutt` to inspect, then either fix the failing job or send the spool to `nexa.system` ntfy via a tiny aliases entry (`root: |/usr/local/bin/spool-to-ntfy.sh`).
@@ -96,7 +96,7 @@ These three are inter-related — picking them up as one campaign is cheaper tha
Pre-step: list every running container with `docker ps --format '{{.Names}}\t{{.Image}}' | grep -i 'postgres\|mariadb\|mysql'` to inventory exactly what's running.
36. **UNAS-integration audit.** `services/<svc>/` on UNAS is the homelab convention but not every container follows it yet. Walk every Arcane stack and check the `volumes:` block. Suspected non-compliant (need verification):
36. **UNAS-integration audit.** `/mnt/pve/unas/services/<svc>/` is the homelab convention (host bind-mount, no driver opts — see #31). Not every container follows it yet. Walk every Arcane stack and check the `volumes:` block: any `- /var/lib/docker/...` or anonymous-volume entry is non-compliant. Suspected non-compliant (need verification):
- `qdrant_scientific` — vector data possibly on local boot disk; **critical to verify** before Phase-3.1 piles on a `nexa_knowledge_text` collection.
- `litellm_db`, `lobe-postgres`, `lobe-redis` — DB containers, persistent state.
- `memos` — the user-facing notes app; loss = data loss.