103 lines
5.5 KiB
YAML
103 lines
5.5 KiB
YAML
# PIHA-specific overrides for kb-postgres (KB spine).
|
||
#
|
||
# WHY PIHA: the KB store must answer queries 24/7. SOLARIA (GPU/compute) is
|
||
# powered down intermittently; PIHA (Raspberry Pi 5, always-on, mains power) is
|
||
# the right home for an always-available spine. Embeddings/models still run on
|
||
# SOLARIA's GPU — only the Postgres+pgvector store lives here.
|
||
#
|
||
# IMAGE / ARCH: pgvector/pgvector:pg16 is multi-arch and publishes a linux/arm64
|
||
# manifest, so it runs natively on the Pi 5 (arm64) — no emulation. We do NOT
|
||
# change the pg16 tag; arch is handled by the manifest list, not the tag.
|
||
#
|
||
# RESOURCE CONTEXT: PIHA has 8 GB RAM, but ~6 GB is already resident
|
||
# (Home Assistant, Immich, monitoring). Only ~2 GB is free (+4 GB swap). This is
|
||
# a RAM-bound box shared with Home Assistant — Postgres MUST NOT starve HA.
|
||
# Everything below is sized to keep kb-postgres's resident set near ~0.5–0.8 GB
|
||
# under normal load, with a hard 1 GB ceiling.
|
||
|
||
services:
|
||
kb-postgres:
|
||
# Hard cgroup ceiling. ~1 GB (not the 4 GB used on SOLARIA). If Postgres ever
|
||
# exceeds this, the cgroup OOM killer restarts the container via Docker —
|
||
# Postgres recovers cleanly via crash recovery — instead of letting the host
|
||
# OOM killer pick a victim (which could be Home Assistant). 1 GB comfortably
|
||
# covers the worst-case allocation below.
|
||
mem_limit: 1g
|
||
# Soft floor for the scheduler: reserve enough that shared_buffers (256 MB)
|
||
# plus connection/backend overhead is not constantly contended under memory
|
||
# pressure, without hard-pinning a full GB away from HA.
|
||
mem_reservation: 512m
|
||
|
||
# Postgres tuning for a tight, shared RAM budget. Passed as server args so we
|
||
# need no mounted postgresql.conf. Defaults (shared_buffers 128 MB, work_mem
|
||
# 4 MB, max_connections 100) assume a dedicated box — far too loose here.
|
||
#
|
||
# shared_buffers=256MB Postgres's own page cache. ~25% of the 1 GB
|
||
# ceiling — the standard rule of thumb. Bigger
|
||
# would crowd HA; smaller hurts cache hit rate
|
||
# for the envelope + pgvector working set.
|
||
# effective_cache_size=512MB Planner hint only (allocates nothing). Tells
|
||
# the planner how much OS+PG cache it can assume
|
||
# for this DB's share of the box, so it favours
|
||
# index scans appropriately. Conservative given
|
||
# the page cache is shared with HA/Immich.
|
||
# work_mem=8MB Per-sort/hash node. With max_connections=30 the
|
||
# worst case is bounded (~30 * a few nodes * 8MB);
|
||
# keeps a runaway analytic query from blowing the
|
||
# budget. Small enough for a Pi, big enough for
|
||
# typical KB lookups.
|
||
# maintenance_work_mem=64MB For VACUUM / CREATE INDEX (incl. building the
|
||
# pgvector ivfflat/hnsw index). One-at-a-time and
|
||
# transient, so a larger value than work_mem is
|
||
# safe and speeds index builds.
|
||
# max_connections=30 KB clients are a handful of agents/jobs, not a
|
||
# web fleet. Capping at 30 bounds per-backend RAM
|
||
# (each backend ~5–10 MB) and the work_mem blast
|
||
# radius. Raise only if a real client count needs it.
|
||
#
|
||
# Sanity check on the ceiling: 256 MB shared_buffers + ~30 backends * ~10 MB
|
||
# overhead (~300 MB) + bounded work_mem spikes stays well under mem_limit=1g.
|
||
command:
|
||
- "postgres"
|
||
- "-c"
|
||
- "shared_buffers=256MB"
|
||
- "-c"
|
||
- "effective_cache_size=512MB"
|
||
- "-c"
|
||
- "work_mem=8MB"
|
||
- "-c"
|
||
- "maintenance_work_mem=64MB"
|
||
- "-c"
|
||
- "max_connections=30"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# DATA PLACEMENT — must land on the NVMe (/home, ~170 GB free), NEVER the SD card.
|
||
#
|
||
# The base compose uses the Docker-managed named volume `kb_postgres_data`, which
|
||
# physically lives under Docker's data-root. On PIHA, Immich already stores its
|
||
# (large) photo library in Docker volumes here, which is only possible if the
|
||
# data-root sits on the NVMe — so the plain named volume should already land on
|
||
# NVMe and is the convention used by other PIHA services (e.g. vikunja).
|
||
#
|
||
# BEFORE FIRST DEPLOY, verify on PIHA:
|
||
# docker info -f '{{.DockerRootDir}}' # expect a path on the NVMe
|
||
# df -h "$(docker info -f '{{.DockerRootDir}}')" # confirm it's the NVMe fs
|
||
#
|
||
# If (and only if) the data-root is NOT on the NVMe, pin the volume explicitly to
|
||
# an NVMe path by uncommenting the block below. The official postgres entrypoint
|
||
# runs as root and chowns PGDATA to the in-container postgres user (uid 999) on
|
||
# startup, so PIHA's host-uid 1004-vs-1000 skew does not apply to PGDATA itself —
|
||
# but the bind *device* directory must pre-exist (Docker will not create it):
|
||
# sudo mkdir -p /home/oskar/homelab-data/kb-postgres
|
||
# sudo chown 1004:1004 /home/oskar/homelab-data/kb-postgres # host owner; PG re-chowns PGDATA to 999 inside
|
||
#
|
||
# volumes:
|
||
# kb_postgres_data:
|
||
# name: kb_postgres_data
|
||
# driver: local
|
||
# driver_opts:
|
||
# type: none
|
||
# o: bind
|
||
# device: /home/oskar/homelab-data/kb-postgres
|
||
# ---------------------------------------------------------------------------
|