Add postgresql to appSpec and its Vault-backed credentials

One Vault path (secret/toolshed/postgres) into two namespaces: the server
needs the credential to initialise, api needs it to connect. Kept in one
file because it is one credential with two consumers — split, they drift,
and drift surfaces as an authentication error that looks nothing like a
config mistake.

The api-side Secret also carries a composed libpq URL, so host, port and
database name live in one place instead of across several environment
variables that can disagree.

Postgres gets its own namespace rather than living inside toolshed, so it
is addressed over cluster DNS like any other platform component and
outlives whatever consumes it.
This commit is contained in:
Mukul Sharma
2026-09-04 16:21:36 +05:30
parent ec049c0671
commit 84f5e27238
2 changed files with 102 additions and 1 deletions
@@ -0,0 +1,82 @@
# PostgreSQL credentials for toolshed, from one Vault path into two
# namespaces: the server needs them to initialise the database, and
# toolshed's api needs them to connect. Kept in one file because they are one
# credential with two consumers — splitting them invites the two drifting
# apart, which fails as an authentication error that looks nothing like a
# config mistake.
#
# Put the credential in Vault BEFORE syncing either of these. External
# Secrets cannot create a Secret for a path that does not exist, and the
# PostgreSQL pod will sit in CreateContainerConfigError until it can:
#
# kubectl -n vault exec -i vault-0 -- sh -lc '
# vault login <root-token> >/dev/null &&
# vault kv put secret/toolshed/postgres \
# username=toolshed \
# password=<a long alphanumeric password>'
#
# Use an alphanumeric password. It is interpolated into a libpq connection
# URL below, where @ : / ? # would need percent-encoding, and a password that
# silently truncates the URL is a genuinely unpleasant thing to debug.
#
# Remember that `kubectl exec` into Vault is unauthenticated by default —
# without the `vault login` the commands fail with a "preflight capability
# check" error that reads like a permissions bug rather than a missing login.
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: postgresql-credentials
namespace: postgres
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: postgresql-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: toolshed/postgres
property: username
- secretKey: password
remoteRef:
key: toolshed/postgres
property: password
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: toolshed-db
namespace: toolshed
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: toolshed-db
creationPolicy: Owner
template:
data:
username: "{{ .username }}"
password: "{{ .password }}"
# Composed here rather than assembled in application code, so the
# host, port and database name live in one place instead of being
# spread across environment variables that can disagree.
#
# sslmode=disable is consistent with this cluster running plain HTTP
# throughout; traffic stays on the pod network. It is the first thing
# to change if this ever leaves the homelab.
url: "postgres://{{ .username }}:{{ .password }}@postgresql.postgres.svc.cluster.local:5432/toolshed?sslmode=disable"
data:
- secretKey: username
remoteRef:
key: toolshed/postgres
property: username
- secretKey: password
remoteRef:
key: toolshed/postgres
property: password
@@ -112,4 +112,23 @@ appSpec:
nameOverride: harbor
namespace: harbor
chartDir: harbor
valuesDir: harbor
valuesDir: harbor
- name: postgresql
# Backs toolshed's control plane. Own namespace rather than living
# inside toolshed, so it is addressed over cluster DNS like any other
# platform component and outlives whatever consumes it:
# postgresql.postgres.svc.cluster.local:5432
#
# nameOverride pinned for the same reason as everything else here —
# without it the rendered Application (and therefore the Helm release
# name, and therefore every object name) becomes
# "postgresql-admin-prd-prd".
#
# Hand-written chart, not Bitnami's: that registry has been actively
# unstable (infra issue #4) and PostgreSQL ships no official chart.
# Requires secretstores/toolshed-postgres-credentials.yaml to have
# synced first — the pod cannot start without the Secret.
nameOverride: postgresql
namespace: postgres
chartDir: postgresql
valuesDir: postgresql