Secrets: ExternalSecret and ExternalName
Tested with: External Secrets Operator 0.10 · AWS Secrets Manager · HashiCorp Vault 1.17 · Kubernetes 1.31
Never commit a database password to a repository, even encrypted. Put it in your secret manager, sync it with
External Secrets Operator (ESO), and let rotation flow through refreshInterval.
Store the credential
Section titled “Store the credential”After zb instances credentials reveal <id> or a rotation, write the pieces to your secret manager as one JSON secret:
{ "host": "pg-7f3k.us-east.databasezy.com", "port": "5432", "username": "app", "password": "...", "database": "app" }SecretStore and ExternalSecret
Section titled “SecretStore and ExternalSecret”apiVersion: external-secrets.io/v1kind: ClusterSecretStoremetadata: name: aws-secretsspec: provider: aws: service: SecretsManager region: us-east-1 auth: jwt: serviceAccountRef: name: external-secrets namespace: external-secrets # IRSA-annotated service accountapiVersion: external-secrets.io/v1kind: ClusterSecretStoremetadata: name: vaultspec: provider: vault: server: https://vault.internal:8200 path: kv version: v2 auth: kubernetes: mountPath: kubernetes role: external-secretsapiVersion: external-secrets.io/v1kind: ClusterSecretStoremetadata: name: gcp-secretsspec: provider: gcpsm: projectID: acme-prod auth: workloadIdentity: clusterLocation: us-east1 clusterName: prod serviceAccountRef: name: external-secrets namespace: external-secretsapiVersion: external-secrets.io/v1kind: ExternalSecretmetadata: name: zb-pg-prod namespace: appspec: refreshInterval: 5m # picks up rotations within the dual-valid window secretStoreRef: kind: ClusterSecretStore name: aws-secrets target: name: zb-pg-prod creationPolicy: Owner template: type: Opaque data: host: "{{ .host }}" port: "{{ .port }}" username: "{{ .username }}" password: "{{ .password }}" database: "{{ .database }}" # Convenience URL for frameworks that want a single variable: DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@{{ .host }}:{{ .port }}/{{ .database }}?sslmode=verify-full&sslrootcert=/etc/databasezy/ca.crt" dataFrom: - extract: key: databasezy/pg-prodPods do not reload environment variables on their own. Either use Reloader
(reloader.stakater.com/auto: "true" on the Deployment) or mount the Secret as files and have the app re-read them.
ExternalName Service
Section titled “ExternalName Service”apiVersion: v1kind: Servicemetadata: name: pg-prod namespace: appspec: type: ExternalName externalName: pg-7f3k.us-east.databasezy.com ports: - { name: postgres, port: 5432 }Kubernetes Secrets vs sealed/encrypted files
Section titled “Kubernetes Secrets vs sealed/encrypted files”Sealed Secrets and SOPS work too and are fine for small teams; you lose automatic rotation and the audit trail of the
secret manager. Whatever you use, the password key should never appear in Git history in plaintext.