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
+20 -28
View File
@@ -95,7 +95,11 @@ Use HuggingFace **text-embeddings-inference** — single Rust binary, ~500 MB im
The active docker manager on this LXC is **Arcane** (visible from Homepage as the running container manager — the LXC was originally provisioned with the Dockge helper-script template, but Dockge is now stale; see [12/#33](./12-optimization-opportunities.md#33)). Paste the stack into Arcane → name it `nexa` → save → start. Don't `docker compose up -d` over SSH; Arcane manages the compose lifecycle.
```yaml
# Nexa stack — paste into Arcane
# Nexa stack — paste into Arcane.
# Storage convention matches the rest of the homelab (verified against
# the running Karakeep stack, Q19): host bind-mount of
# /mnt/pve/unas/services/<svc>/<vol>. No volume-driver, no CIFS, no
# credentials in the compose — the LXC's NFS mount is already there.
services:
nexa-embed:
image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.5
@@ -105,37 +109,25 @@ services:
ports:
- "127.0.0.1:8080:80"
volumes:
- tei-cache:/data
- /mnt/pve/unas/services/nexa/tei-cache:/data
env_file:
- .env
volumes:
# SMB volume on UNAS — sidesteps NFS+unprivileged-LXC permission issues
# (the same kind that bit the Nextcloud rollout). Stores embedding model
# caches, Qdrant snapshots, future GraphDB exports.
#
# The exact mount block (driver, share name, credential injection) MUST
# be copied verbatim from an existing stack on the same docker host —
# services/<svc>/ on UNAS is the homelab-wide docker config store and
# every container already follows the same SMB pattern. See Q19 in
# docs/11. The block below is the *shape* but the share name "<unas-share>"
# is a placeholder until verified against e.g. services/karakeep/compose.
tei-cache:
driver: local
driver_opts:
type: cifs
device: //192.168.1.31/<unas-share>/services/nexa/tei-cache
o: "username=${UNAS_USER},password=${UNAS_PASS},uid=1000,gid=1000,vers=3.0,iocharset=utf8"
networks:
default:
external: true
name: nexa
networks: {}
```
Memory budget: ~1.1 GB resident. First start downloads `bge-m3` (~1 GB) into the SMB-backed volume; subsequent restarts are instant.
Pre-deploy step on the docker LXC (one-time):
> **Why SMB and not the Proxmox `/mnt/pve/unas` NFS mount?** The host-side NFS mount works fine for host admin, but **unprivileged LXC + NFS + per-container writes** historically breaks (user confirmed: "had it with Nextcloud"). Mounting SMB **directly as a docker volume** with explicit `username=`/`uid=`/`gid=` bypasses LXC uid-mapping entirely — the docker daemon is root inside the LXC so it can mount, and the container sees a regular volume with the right ownership.
>
> Add `UNAS_USER` / `UNAS_PASS` to the stack's `.env` (Arcane provides an editor for this), or — better — store them in Vaultwarden and inject via a secrets agent (see [12/#11](./12-optimization-opportunities.md#11)).
```bash
mkdir -p /mnt/pve/unas/services/nexa/{tei-cache,qdrant,graphdb}
mkdir -p /mnt/pve/unas/backup/nexa/snapshots/{qdrant,graphdb}
```
Memory budget: ~1.1 GB resident. First start downloads `bge-m3` (~1 GB) into `/mnt/pve/unas/services/nexa/tei-cache/`; subsequent restarts are instant.
Secrets (`SAIA_API_KEY`, `MEMOS_API_KEY`, `QDRANT_API_KEY`, `NC_APP_PASSWORD`) go in the stack's `.env` next to the compose — same pattern Karakeep uses (`env_file: .env`). Arcane has an editor for it. Vaultwarden becomes the source-of-truth long-term ([12/#11](./12-optimization-opportunities.md#11)) but isn't required for the first cut.
> **Why bind-mount and not SMB?** Earlier drafts of this doc proposed an SMB-via-docker-volume pattern because of the user's "had it with Nextcloud" experience. The Karakeep stack confirms the actual convention is the simpler one: **host bind-mount of the LXC's existing `/mnt/pve/unas` NFS mount**. The Nextcloud failure was Nextcloud-specific (its setup tooling chowns the data dir to `www-data`, which fails against `root_squash` exports) and doesn't apply to normal containers. SMB-as-docker-volume stays documented in [12/#27](./12-optimization-opportunities.md#27) only as an escape hatch *if* a future service hits Nextcloud-style issues — Nexa doesn't, so we don't use it.
Register it inside LiteLLM (admin UI → Models) with the OpenAI-compatible adapter: