Argo CD and Helm patterns
Tested with: Argo CD 2.12 · Helm 3.16 · External Secrets Operator 0.10
Chart values: reference, never embed
Section titled “Chart values: reference, never embed”Keep the credential out of values. The chart takes the name of a Secret and the instance host; ESO creates the Secret (see Secrets).
database: existingSecret: zb-pg-prod # created by ExternalSecret host: pg-7f3k.us-east.databasezy.com port: 5432 name: app sslmode: verify-full caConfigMap: databasezy-caenv: - name: PGHOST value: {{ .Values.database.host | quote }} - name: PGPORT value: {{ .Values.database.port | quote }} - name: PGDATABASE value: {{ .Values.database.name | quote }} - name: PGSSLMODE value: {{ .Values.database.sslmode | quote }} - name: PGSSLROOTCERT value: /etc/databasezy/ca.crt - name: PGUSER valueFrom: { secretKeyRef: { name: {{ .Values.database.existingSecret }}, key: username } } - name: PGPASSWORD valueFrom: { secretKeyRef: { name: {{ .Values.database.existingSecret }}, key: password } }volumeMounts: - { name: databasezy-ca, mountPath: /etc/databasezy, readOnly: true }volumes: - name: databasezy-ca configMap: { name: {{ .Values.database.caConfigMap }} }ExternalSecret as part of the chart
Section titled “ExternalSecret as part of the chart”Ship the ExternalSecret in the chart so an environment is one values file:
{{- if .Values.database.externalSecret.enabled }}apiVersion: external-secrets.io/v1kind: ExternalSecretmetadata: name: {{ .Values.database.existingSecret }}spec: refreshInterval: 5m secretStoreRef: { kind: ClusterSecretStore, name: {{ .Values.database.externalSecret.store }} } target: { name: {{ .Values.database.existingSecret }} } dataFrom: - extract: { key: {{ .Values.database.externalSecret.key }} }{{- end }}Argo CD Application with a PreSync migration
Section titled “Argo CD Application with a PreSync migration”apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: api-prod namespace: argocdspec: project: prod source: repoURL: https://github.com/acme/charts path: api targetRevision: main helm: valueFiles: [values-prod.yaml] destination: server: https://kubernetes.default.svc namespace: app syncPolicy: automated: { prune: true, selfHeal: true } syncOptions: [CreateNamespace=true, ServerSideApply=true]metadata: name: migrate-{{ .Values.image.tag }} annotations: argocd.argoproj.io/hook: PreSync argocd.argoproj.io/hook-delete-policy: BeforeHookCreationArgo waits for the PreSync Job before applying the rest; a failed migration leaves the previous Deployment running.
Per-environment instances
Section titled “Per-environment instances”One instance per environment, never one database per environment inside a shared instance, so quotas, backups,
allow-lists and audit are separate. On the free tier use f0 instances for preview; on paid plans create them
from CI with the CLI and delete them when the branch closes:
zb instances create --engine postgres --size s1 --region us-east --name "pg-pr-$PR" --project previews --waitzb --json instances credentials reveal "pg-pr-$PR" | aws secretsmanager put-secret-value \ --secret-id "databasezy/pg-pr-$PR" --secret-string file:///dev/stdin# ... on close:zb instances delete "pg-pr-$PR" --yes