Post

PostgreSQL

PostgreSQL

PostgreSQL

Purpose

Shared PostgreSQL 16 instance used by other homelab services (Media Indexer, Postgres MCP, the homelab knowledge base, and connections registered in pgAdmin). Runs in the default namespace of the k3s cluster.

The image is the pgvector build of PostgreSQL 16, so the knowledge base can store embeddings in the same instance. It’s the Debian trixie variant: its glibc matches the collation version the existing databases were created with (2.41), so switching images didn’t trigger collation-mismatch warnings or need a reindex.

Deployment

ItemValue
Imagepgvector/pgvector:0.8.7-pg16-trixie (PostgreSQL 16 with the pgvector extension, Debian trixie build)
Namespacedefault
Replicas1 (Recreate strategy)
ServiceLoadBalancer, port 5432 (MetalLB-assigned external IP 192.0.2.33)
Storagepostgres-data-zfs PVC, 5Gi, local-zfs storage class, mounted at /var/lib/postgresql/data
Data directoryPGDATA=/var/lib/postgresql/data/pgdata (subdirectory of the PVC mount, avoiding issues with lost+found in the volume root)
CredentialsPOSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB loaded in full via envFrom.secretRef from the postgres-secret Secret
Auto-reloadreloader.stakater.com/auto: "true"

Storage

Uses a node-local local-zfs volume: a directory on that node’s ZFS-backed volume (a zvol on the TrueNAS NVMe pool, attached to the k3s LXC node), so the pod is scheduled where its volume is. Losing that node means Postgres waits for it to come back; the data itself is protected by ZFS snapshots of the node’s volume, replicated off-site every night.

Access

The LoadBalancer Service exposes Postgres directly on the LAN at 192.0.2.33:5432 — there’s no ingress/TLS layer in front of it, since the Postgres wire protocol isn’t HTTP. Consumers on the cluster (Postgres MCP, Media Indexer) instead reach it via the in-cluster DNS name postgres.default.svc.cluster.local:5432.

This post is licensed under CC BY 4.0 by the author.