PostgreSQL
Production-ready PostgreSQL deployment with support for standalone and streaming replication architectures, structured access controls, TLS, External Secrets Operator, optional replication slots, and production hardening controls.
Key Features
- Standalone and replication — Single instance or primary with streaming replicas
- Automatic initialization — Init scripts, extensions, and custom configuration
- S3 backup — Scheduled backups to S3-compatible storage via CronJob
- Metrics — Prometheus exporter with ServiceMonitor
- Security — Non-root containers, TLS support with private-key permission normalization, structured
pg_hba.confCIDRs, NetworkPolicy controls, and ServiceAccount token control - Replication slots — Optional physical slots with WAL retention limits for safer replica catch-up behavior
- External Secrets Operator — Optional
external-secrets.io/v1resources for auth, TLS, and backup credentials - Persistent storage — Configurable PVCs with storage class selection
Security Scan
| Framework | Score |
|---|---|
| MITRE + NSA + SOC2 | 93% |
Security posture is strong. Remaining findings are documented product exceptions for opt-in NetworkPolicy and PostgreSQL’s writable runtime filesystem.
Architecture
Standalone
Single PostgreSQL instance with persistent storage and optional S3 backup.
Replication
Primary with streaming replicas, read-only service, and backup CronJob.
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install my-pg helmforge/postgresql
OCI registry:
helm install my-pg oci://ghcr.io/helmforgedev/helm/postgresql
Deployment Examples
# values.yaml
architecture: standalone
auth:
postgresPassword: 'my-secret-password'
database: myapp
username: myuser
password: 'user-password'
standalone:
persistence:
enabled: true
size: 20Gi
metrics:
enabled: true
serviceMonitor:
enabled: true# values.yaml
architecture: replication
auth:
postgresPassword: 'my-secret-password'
database: myapp
username: myuser
password: 'user-password'
replicationPassword: 'repl-password'
replication:
primary:
persistence:
enabled: true
size: 20Gi
readReplicas:
replicaCount: 2
persistence:
enabled: true
size: 20Gi# values.yaml
architecture: standalone
auth:
postgresPassword: 'my-secret-password'
database: myapp
standalone:
persistence:
enabled: true
size: 20Gi
backup:
enabled: true
schedule: '0 3 * * *'
s3:
endpoint: https://s3.us-east-2.amazonaws.com
bucket: my-pg-backups
region: us-east-2
credentialsFromPodIdentity: trueThe example above uses EKS Pod Identity and therefore does not add an IRSA
annotation. For IRSA, keep credentialsFromPodIdentity: true and annotate the
backup ServiceAccount with your own role ARN:
backup:
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/replace-with-your-backup-role# values.yaml
architecture: standalone
auth:
postgresPassword: 'my-secret-password'
database: myapp
standalone:
persistence:
enabled: true
size: 20Gi
tls:
enabled: true
existingSecret: postgresql-tls
sslMode: require
volumePermissions:
enabled: true# values.yaml - production baseline with replication, TLS, slots, backup, and policies
architecture: replication
auth:
existingSecret: postgresql-auth
database: app
username: app
config:
localAuthMethod: scram-sha-256
allowedClientCIDRs:
- 10.42.0.0/16
allowedReplicationCIDRs:
- 10.42.0.0/16
replication:
primary:
persistence:
size: 100Gi
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: '2'
memory: 2Gi
readReplicas:
replicaCount: 2
persistence:
size: 100Gi
wal:
keepSize: 2GB
maxReplicationSlots: 10
maxSlotWalKeepSize: 8GB
walSenderTimeout: 60s
slots:
enabled: true
tls:
enabled: true
existingSecret: postgresql-tls
sslMode: require
volumePermissions:
enabled: true
metrics:
enabled: true
serviceMonitor:
enabled: true
networkPolicy:
enabled: true
egress:
enabled: true
allowDNS: true
allowSameNamespacePostgreSQL: true
allowHTTPS: true
serviceAccount:
create: true
automountServiceAccountToken: false
backup:
enabled: true
s3:
endpoint: https://s3.example.com
bucket: postgresql-backups
existingSecret: postgresql-backup# values.yaml - render ExternalSecret resources for existing ESO clusters
auth:
existingSecret: postgresql-auth
tls:
enabled: true
existingSecret: postgresql-tls
backup:
enabled: true
s3:
endpoint: https://s3.example.com
bucket: postgresql-backups
existingSecret: postgresql-backup
externalSecrets:
enabled: true
apiVersion: external-secrets.io/v1
secretStoreRef:
name: platform-secrets
kind: ClusterSecretStore
auth:
enabled: true
targetName: postgresql-auth
postgresPasswordRemoteRef:
key: prod/postgresql
property: postgres-password
userPasswordRemoteRef:
key: prod/postgresql
property: user-password
replicationPasswordRemoteRef:
key: prod/postgresql
property: replication-password
tls:
enabled: true
targetName: postgresql-tls
certRemoteRef:
key: prod/postgresql-tls
property: tls.crt
keyRemoteRef:
key: prod/postgresql-tls
property: tls.key
caRemoteRef:
key: prod/postgresql-tls
property: ca.crt
backup:
enabled: true
targetName: postgresql-backup
accessKeyRemoteRef:
key: prod/postgresql-backup
property: access-key
secretKeyRemoteRef:
key: prod/postgresql-backup
property: secret-keyThe chart does not install External Secrets Operator and does not create a SecretStore or ClusterSecretStore.
Enable this only when the operator and referenced store already exist.
The backup workload identity mode never renders static S3 credentials. EKS Pod
Identity requires an association for the dedicated backup ServiceAccount. IRSA
uses the role annotation shown in the backup example. Keep
backup.serviceAccount.automountServiceAccountToken=false unless the backup
pod must call the Kubernetes API; AWS identity injection uses a separate
projected token.
Production Path
The default install remains intentionally suitable for local development and disposable test clusters. A production
deployment should explicitly set existing Secrets or External Secrets, storage, resources, config.allowedClientCIDRs,
config.allowedReplicationCIDRs, TLS, metrics, backup, and NetworkPolicy behavior.
Use the structured CIDR lists first. config.pgHbaEntries and raw config.pgHba remain escape hatches for advanced
cases, but production baselines should not depend on a full raw pg_hba.conf override.
Replication slots are disabled by default for compatibility. If replication.slots.enabled=true, also set WAL limits
such as replication.wal.maxSlotWalKeepSize so a stalled replica cannot retain unbounded WAL.
Security and observability boundaries
The chart configures the main StatefulSet containers to run as non-root with dropped capabilities, and can configure
TLS, structured pg_hba.conf rules, External Secrets, and NetworkPolicies. Operators still own certificate issuance,
SecretStore configuration, credential rotation, backup encryption, and restrictive client and replication CIDRs.
NetworkPolicy enforcement requires a compatible cluster network plugin, and the official PostgreSQL image keeps a
writable runtime filesystem.
Metrics and ServiceMonitor resources are optional and require an existing Prometheus stack. The chart does not install Prometheus, log collection, dashboards, tracing, or alert rules. Production operators should alert on connection saturation, replication lag, retained WAL, storage growth, backup freshness, restore tests, and credential expiry.
Known limitations
Replication provides a fixed primary and read replicas; it does not perform promotion, fencing, failback, or automatic read/write routing. The backup CronJob creates logical dumps and does not provide continuous archiving or point-in-time recovery. Major PostgreSQL upgrades, storage migrations, connection pooling, and reconfiguration after a primary failure require external workflows or an operator.
Operational Notes
- The chart keeps internal health checks, metrics, and built-in backup on PostgreSQL’s standard
postgresdatabase. - If a reused PVC contains a valid data directory but is missing the default
postgresdatabase, the primary pod repairs that database during startup before normal readiness checks rely on it. auth.databaseremains the application bootstrap database. It is created only on first initialization of a fresh data directory.- Set
initdb.runDefaultScript=falsewhen custom or externally managed init scripts should run without the chart-generated application and replication user bootstrap script. docker-entrypoint-initdb.dscripts only run when the data directory is empty. Existing PVCs keep their current roles and databases during upgrades.
Dual-stack Networking
PostgreSQL Services accept Kubernetes dual-stack configuration. By default, both service.ipFamilyPolicy and service.ipFamilies are unset and the chart inherits whatever the cluster advertises (matching prior behavior). Setting them propagates to every chart-managed Service: client, primary, replicas, metrics, and the headless StatefulSet services.
service:
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
PreferDualStack is the safer choice for clusters that may be single- or dual-stack: omit ipFamilies and the cluster auto-populates whatever it supports. Set ipFamilies explicitly only when the cluster is configured for both families — the Kubernetes API rejects an explicit family the cluster does not advertise. RequireDualStack enforces both.
Configuration Reference
This reference covers the complete values.yaml surface for the PostgreSQL chart.
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
architecture |
string | standalone |
Deployment architecture: standalone or replication. |
nameOverride |
string | "" |
Override the chart name. |
fullnameOverride |
string | "" |
Override the full release name. |
commonLabels |
object | {} |
Extra labels added to all rendered resources. |
commonAnnotations |
object | {} |
Annotations added to every chart-owned resource. |
clusterDomain |
string | cluster.local |
Kubernetes cluster domain used in generated DNS names. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository |
string | docker.io/library/postgres |
PostgreSQL runtime image repository. |
image.tag |
string | "18.6-trixie" |
PostgreSQL runtime image tag. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy for PostgreSQL containers. |
imagePullSecrets |
array | [] |
Optional image pull secrets for private registries. |
Authentication
| Parameter | Type | Default | Description |
|---|---|---|---|
auth.postgresPassword |
string | "" |
PostgreSQL superuser password when not using an existing secret. |
auth.database |
string | app |
Application database created on first bootstrap. |
auth.username |
string | app |
Application username created on first bootstrap. |
auth.password |
string | "" |
Application user password when not using an existing secret. |
auth.replicationUsername |
string | replicator |
Replication username used by read replicas. |
auth.replicationPassword |
string | "" |
Replication password when not using an existing secret. |
auth.existingSecret |
string | "" |
Existing secret containing PostgreSQL passwords. |
auth.existingSecretPostgresPasswordKey |
string | postgres-password |
Secret key holding the PostgreSQL superuser password. |
auth.existingSecretUserPasswordKey |
string | user-password |
Secret key holding the application user password. |
auth.existingSecretReplicationPasswordKey |
string | replication-password |
Secret key holding the replication user password. |
PostgreSQL Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
config.localAuthMethod |
string | scram-sha-256 |
Authentication method for local Unix-socket connections. |
config.allowedClientCIDRs |
array | ["0.0.0.0/0", "::/0"] |
CIDRs allowed to connect to regular PostgreSQL databases. |
config.allowedReplicationCIDRs |
array | ["0.0.0.0/0", "::/0"] |
CIDRs allowed to connect to the replication pseudo-database. |
config.preset |
string | none |
Optional PostgreSQL tuning preset: none, small, medium, or large. |
config.postgresql |
string | "" |
Raw postgresql.conf content appended after generated settings. |
config.pgHbaEntries |
array | [] |
Structured pg_hba.conf entries appended after generated defaults. |
config.pgHba |
string | "" |
Raw pg_hba.conf content appended after generated rules. |
TLS
| Parameter | Type | Default | Description |
|---|---|---|---|
tls.enabled |
boolean | false |
Enable TLS for PostgreSQL and internal clients. |
tls.existingSecret |
string | "" |
Existing secret containing server TLS material. |
tls.certFilename |
string | tls.crt |
Certificate filename inside the TLS secret. |
tls.keyFilename |
string | tls.key |
Private key filename inside the TLS secret. |
tls.caFilename |
string | ca.crt |
CA certificate filename inside the TLS secret. |
tls.sslMode |
string | require |
libpq SSL mode used by internal probes, replication, and exporter. |
tls.minProtocolVersion |
string | TLSv1.2 |
Minimum accepted PostgreSQL TLS protocol version. |
tls.volumePermissions.enabled |
boolean | false |
Copy TLS material to an owned emptyDir and restrict the private key to 0600. |
tls.volumePermissions.securityContext.runAsUser |
integer | 0 |
UID used by the TLS permission init container. |
tls.volumePermissions.securityContext.runAsNonRoot |
boolean | false |
Permit the permission init container to run as root. |
tls.volumePermissions.securityContext.allowPrivilegeEscalation |
boolean | false |
Disable privilege escalation for the permission init container. |
tls.volumePermissions.securityContext.capabilities.add |
array | ["CHOWN", "FOWNER"] |
Capabilities required to normalize TLS file ownership. |
tls.volumePermissions.securityContext.capabilities.drop |
array | ["ALL"] |
Capabilities dropped before the required additions. |
Initialization
| Parameter | Type | Default | Description |
|---|---|---|---|
initdb.runDefaultScript |
boolean | true |
Run the chart-generated app database, app user, and replication user script. |
initdb.scripts |
object | {} |
Additional scripts written into docker-entrypoint-initdb.d. |
initdb.existingConfigMap |
string | "" |
Existing ConfigMap mounted into docker-entrypoint-initdb.d. |
Standalone
| Parameter | Type | Default | Description |
|---|---|---|---|
standalone.resourcesPreset |
string | small |
Optional resource preset for standalone mode. |
standalone.persistence.enabled |
boolean | true |
Enable a PVC for standalone mode. |
standalone.persistence.storageClass |
string | "" |
StorageClass for the standalone PVC. |
standalone.persistence.accessModes |
array | ["ReadWriteOnce"] |
Access modes for the standalone PVC. |
standalone.persistence.size |
string | 8Gi |
PVC size for standalone mode. |
standalone.persistence.retentionPolicy.whenDeleted |
string | "" |
PVC policy when the StatefulSet is deleted: Retain, Delete, or cluster default. |
standalone.persistence.retentionPolicy.whenScaled |
string | "" |
PVC policy when the StatefulSet scales down: Retain, Delete, or cluster default. |
standalone.resources |
object | {} |
Explicit resource requests and limits for standalone mode. |
Replication - Primary
| Parameter | Type | Default | Description |
|---|---|---|---|
replication.primary.resourcesPreset |
string | small |
Optional resource preset for the primary pod. |
replication.primary.persistence.enabled |
boolean | true |
Enable PVCs for the primary StatefulSet. |
replication.primary.persistence.storageClass |
string | "" |
StorageClass for primary PVCs. |
replication.primary.persistence.accessModes |
array | ["ReadWriteOnce"] |
Access modes for primary PVCs. |
replication.primary.persistence.size |
string | 20Gi |
PVC size for the primary pod. |
replication.primary.persistence.retentionPolicy.whenDeleted |
string | "" |
Primary PVC policy when the StatefulSet is deleted. |
replication.primary.persistence.retentionPolicy.whenScaled |
string | "" |
Primary PVC policy when the StatefulSet scales down. |
replication.primary.resources |
object | {} |
Explicit resource requests and limits for the primary pod. |
replication.primary.probes.requireWritable |
boolean | true |
In replication mode, require primary readiness to confirm the pod is not in recovery mode. |
Replication - Read Replicas
| Parameter | Type | Default | Description |
|---|---|---|---|
replication.readReplicas.resourcesPreset |
string | small |
Optional resource preset for replica pods. |
replication.readReplicas.replicaCount |
integer | 2 |
Number of asynchronous read replica pods. |
replication.readReplicas.persistence.enabled |
boolean | true |
Enable PVCs for replica StatefulSets. |
replication.readReplicas.persistence.storageClass |
string | "" |
StorageClass for replica PVCs. |
replication.readReplicas.persistence.accessModes |
array | ["ReadWriteOnce"] |
Access modes for replica PVCs. |
replication.readReplicas.persistence.size |
string | 20Gi |
PVC size for each replica. |
replication.readReplicas.persistence.retentionPolicy.whenDeleted |
string | "" |
Replica PVC policy when the StatefulSet is deleted. |
replication.readReplicas.persistence.retentionPolicy.whenScaled |
string | "" |
Replica PVC policy when the StatefulSet scales down. |
replication.readReplicas.resources |
object | {} |
Explicit resource requests and limits for replica pods. |
replication.readReplicas.probes.requireRecoveryMode |
boolean | true |
In replication mode, require replica readiness to confirm the pod is in recovery mode. |
Replication - WAL, PDB, and Scheduling
On Kubernetes 1.26, a Delete PVC retention policy requires the
StatefulSetAutoDeletePVC feature gate on both the API server and controller
manager. Verify that cluster setting before relying on automatic deletion.
| Parameter | Type | Default | Description |
|---|---|---|---|
replication.wal.keepSize |
string | 512MB |
WAL retention kept locally for replicas and recovery workflows. |
replication.wal.maxSenders |
integer | 10 |
Maximum WAL sender processes. |
replication.wal.maxReplicationSlots |
integer | 10 |
Maximum replication slots. |
replication.wal.maxSlotWalKeepSize |
string | "" |
Maximum WAL retained by replication slots before invalidation. |
replication.wal.idleReplicationSlotTimeout |
string | "" |
Timeout for inactive replication slots before invalidation. |
replication.slots.enabled |
boolean | false |
Use deterministic physical replication slots for read replicas. |
replication.slots.namePrefix |
string | replica |
Prefix used to build per-replica slot names. |
replication.pdb.enabled |
boolean | true |
Enable a PodDisruptionBudget by default in replication mode. |
replication.pdb.minAvailable |
integer | 1 |
Minimum available pods for the replication PDB. |
replication.pdb.maxUnavailable |
string | "" |
Maximum unavailable pods for the replication PDB. |
replication.scheduling.enableDefaultPodAntiAffinity |
boolean | true |
Enable opinionated anti-affinity defaults in replication mode. |
replication.scheduling.enableDefaultTopologySpread |
boolean | true |
Enable opinionated topology spread defaults in replication mode. |
replication.scheduling.topologyKey |
string | kubernetes.io/hostname |
Topology key used by the default replication spread rules. |
Service
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type |
string | ClusterIP |
Service type for PostgreSQL client access. |
service.annotations |
object | {} |
Annotations applied to the client Service. |
service.primaryAnnotations |
object | {} |
Annotations applied to the dedicated primary Service. |
service.replicasAnnotations |
object | {} |
Annotations applied to the dedicated replicas Service. |
service.port |
integer | 5432 |
PostgreSQL listener port. |
service.metricsPort |
integer | 9187 |
Metrics port exposed when metrics are enabled. |
service.ipFamilyPolicy |
string | omitted | Service IP family policy: SingleStack, PreferDualStack, or RequireDualStack. Omit for cluster default. |
service.ipFamilies |
array | omitted | Ordered list of IP families (IPv4, IPv6). Omit for cluster default. |
Metrics
| Parameter | Type | Default | Description |
|---|---|---|---|
metrics.enabled |
boolean | false |
Enable the postgres_exporter sidecar. |
metrics.resourcesPreset |
string | small |
Optional resource preset for postgres_exporter. |
metrics.service.annotations |
object | {} |
Annotations applied to metrics Services. |
metrics.image.repository |
string | quay.io/prometheuscommunity/postgres-exporter |
Metrics sidecar image repository. |
metrics.image.tag |
string | "v0.18.0" |
Metrics sidecar image tag. |
metrics.image.pullPolicy |
string | IfNotPresent |
Image pull policy for postgres_exporter. |
metrics.resources |
object | {} |
Explicit resource requests and limits for postgres_exporter. |
metrics.serviceMonitor.enabled |
boolean | false |
Create a ServiceMonitor for Prometheus Operator. |
metrics.serviceMonitor.interval |
string | 30s |
Scrape interval for the ServiceMonitor. |
metrics.serviceMonitor.labels |
object | {} |
Extra labels applied to the ServiceMonitor. |
Backup
| Parameter | Type | Default | Description |
|---|---|---|---|
backup.enabled |
boolean | false |
Enable the built-in backup CronJob. |
backup.schedule |
string | "0 3 * * *" |
Backup schedule in cron format. |
backup.suspend |
boolean | false |
Suspend backup execution. |
backup.concurrencyPolicy |
string | Forbid |
CronJob concurrency policy. |
backup.successfulJobsHistoryLimit |
integer | 3 |
Successful backup Jobs retained by the CronJob. |
backup.failedJobsHistoryLimit |
integer | 3 |
Failed backup Jobs retained by the CronJob. |
backup.backoffLimit |
integer | 1 |
Job backoff limit for backup execution. |
backup.archivePrefix |
string | postgresql |
Prefix used in generated backup archive names. |
backup.resources.requests.cpu |
string | 100m |
CPU requested by backup dump and upload containers. |
backup.resources.requests.memory |
string | 128Mi |
Memory requested by backup dump and upload containers. |
backup.resources.limits.cpu |
string | 500m |
CPU limit for backup dump and upload containers. |
backup.resources.limits.memory |
string | 512Mi |
Memory limit for backup dump and upload containers. |
backup.images.uploader.repository |
string | docker.io/helmforge/mc |
Uploader image repository for S3 copies. |
backup.images.uploader.tag |
string | "1.0.0" |
Uploader image tag. |
backup.images.uploader.pullPolicy |
string | IfNotPresent |
Pull policy for the uploader image. |
backup.images.postgresql.repository |
string | docker.io/library/postgres |
PostgreSQL client image used for backup jobs. |
backup.images.postgresql.tag |
string | "18.6-trixie" |
PostgreSQL client image tag used for backup jobs. |
backup.images.postgresql.pullPolicy |
string | IfNotPresent |
Pull policy for the PostgreSQL backup image. |
backup.serviceAccount.create |
boolean | true |
Create a dedicated ServiceAccount for Pod Identity or IRSA. |
backup.serviceAccount.name |
string | "" |
Existing dedicated backup ServiceAccount when creation is disabled. |
backup.serviceAccount.annotations |
object | {} |
Workload identity annotations, such as an IRSA role ARN. |
backup.serviceAccount.automountServiceAccountToken |
boolean | false |
Mount Kubernetes API credentials into backup pods. |
backup.podSecurityContext.fsGroup |
integer | 999 |
Shared group for the backup work directory. |
backup.podSecurityContext.fsGroupChangePolicy |
string | OnRootMismatch |
Avoid unnecessary ownership changes. |
backup.podSecurityContext.seccompProfile.type |
string | RuntimeDefault |
Seccomp profile for backup Jobs. |
backup.databaseSecurityContext.runAsNonRoot |
boolean | true |
Require the dump container to run as non-root. |
backup.databaseSecurityContext.runAsUser |
integer | 999 |
UID for the pg_dumpall container. |
backup.databaseSecurityContext.runAsGroup |
integer | 999 |
GID for the pg_dumpall container. |
backup.databaseSecurityContext.allowPrivilegeEscalation |
boolean | false |
Disable privilege escalation for the dump container. |
backup.databaseSecurityContext.readOnlyRootFilesystem |
boolean | true |
Use a read-only root filesystem for the dump container. |
backup.databaseSecurityContext.capabilities.drop |
array | ["ALL"] |
Drop Linux capabilities from the dump container. |
backup.uploaderSecurityContext.runAsNonRoot |
boolean | true |
Require the uploader to run as non-root. |
backup.uploaderSecurityContext.runAsUser |
integer | 65532 |
UID for the S3 uploader. |
backup.uploaderSecurityContext.runAsGroup |
integer | 65532 |
GID for the S3 uploader. |
backup.uploaderSecurityContext.allowPrivilegeEscalation |
boolean | false |
Disable privilege escalation for the uploader. |
backup.uploaderSecurityContext.readOnlyRootFilesystem |
boolean | true |
Use a read-only root filesystem for the uploader. |
backup.uploaderSecurityContext.capabilities.drop |
array | ["ALL"] |
Drop Linux capabilities from the uploader. |
backup.s3.endpoint |
string | "" |
S3-compatible endpoint URL. |
backup.s3.bucket |
string | "" |
Target bucket name. |
backup.s3.prefix |
string | postgresql |
Optional key prefix inside the bucket. |
backup.s3.createBucketIfNotExists |
boolean | true |
Create the bucket automatically when it does not exist. |
backup.s3.existingSecret |
string | "" |
Existing secret containing backup access and secret keys. |
backup.s3.existingSecretAccessKeyKey |
string | access-key |
Secret key name for the S3 access key. |
backup.s3.existingSecretSecretKeyKey |
string | secret-key |
Secret key name for the S3 secret key. |
backup.s3.accessKey |
string | "" |
Inline S3 access key when not using an existing secret. |
backup.s3.secretKey |
string | "" |
Inline S3 secret key when not using an existing secret. |
backup.s3.region |
string | "" |
AWS region required for workload identity. |
backup.s3.credentialsFromPodIdentity |
boolean | false |
Use EKS Pod Identity or IRSA instead of static S3 keys. |
backup.database.pgDumpAllArgs |
string | "--clean --if-exists" |
Extra arguments passed to pg_dumpall. |
NetworkPolicy
| Parameter | Type | Default | Description |
|---|---|---|---|
networkPolicy.enabled |
boolean | false |
Create a NetworkPolicy for PostgreSQL pods. |
networkPolicy.ingress.allowSameNamespace |
boolean | true |
Allow ingress from the same namespace. |
networkPolicy.ingress.extraFrom |
array | [] |
Additional ingress from rules appended to the generated policy. |
networkPolicy.egress.enabled |
boolean | false |
Add explicit egress rules to the NetworkPolicy. |
networkPolicy.egress.allowDNS |
boolean | true |
Allow DNS egress on TCP/UDP 53. |
networkPolicy.egress.allowSameNamespacePostgreSQL |
boolean | true |
Allow PostgreSQL egress to same-namespace pods. |
networkPolicy.egress.allowHTTPS |
boolean | true |
Allow HTTPS egress for S3-compatible backup endpoints. |
networkPolicy.egress.extraTo |
array | [] |
Additional egress to rules appended to the policy. |
Pod Disruption Budget
| Parameter | Type | Default | Description |
|---|---|---|---|
pdb.enabled |
boolean | false |
Enable a PodDisruptionBudget outside replication defaults. |
pdb.minAvailable |
integer | 1 |
Minimum available pods when the PDB is enabled. |
pdb.maxUnavailable |
string | "" |
Maximum unavailable pods when the PDB is enabled. |
Service Account
| Parameter | Type | Default | Description |
|---|---|---|---|
serviceAccount.create |
boolean | false |
Create a dedicated ServiceAccount. |
serviceAccount.name |
string | "" |
Override the ServiceAccount name. |
serviceAccount.annotations |
object | {} |
Annotations applied to the ServiceAccount. |
serviceAccount.automountServiceAccountToken |
boolean | false |
Mount Kubernetes API credentials into PostgreSQL pods. |
Probes
| Parameter | Type | Default | Description |
|---|---|---|---|
livenessProbe.enabled |
boolean | true |
Enable the liveness probe. |
livenessProbe.initialDelaySeconds |
integer | 60 |
Liveness probe initial delay. |
livenessProbe.periodSeconds |
integer | 20 |
Liveness probe period. |
livenessProbe.timeoutSeconds |
integer | 5 |
Liveness probe timeout. |
livenessProbe.failureThreshold |
integer | 6 |
Liveness probe failure threshold. |
readinessProbe.enabled |
boolean | true |
Enable the readiness probe. |
readinessProbe.initialDelaySeconds |
integer | 20 |
Readiness probe initial delay. |
readinessProbe.periodSeconds |
integer | 10 |
Readiness probe period. |
readinessProbe.timeoutSeconds |
integer | 5 |
Readiness probe timeout. |
readinessProbe.failureThreshold |
integer | 6 |
Readiness probe failure threshold. |
startupProbe.enabled |
boolean | true |
Enable the startup probe. |
startupProbe.initialDelaySeconds |
integer | 10 |
Startup probe initial delay. |
startupProbe.periodSeconds |
integer | 10 |
Startup probe period. |
startupProbe.timeoutSeconds |
integer | 5 |
Startup probe timeout. |
startupProbe.failureThreshold |
integer | 60 |
Startup probe failure threshold. |
Scheduling and Security
| Parameter | Type | Default | Description |
|---|---|---|---|
podSecurityContext.fsGroup |
integer | 999 |
Filesystem group applied to PostgreSQL pods. |
podSecurityContext.fsGroupChangePolicy |
string | OnRootMismatch |
Filesystem group change policy. |
podSecurityContext.seccompProfile.type |
string | RuntimeDefault |
Seccomp profile applied at pod level. |
securityContext.runAsUser |
integer | 999 |
Runtime UID for PostgreSQL containers. |
securityContext.runAsGroup |
integer | 999 |
Runtime GID for PostgreSQL containers. |
securityContext.runAsNonRoot |
boolean | true |
Require containers to run as non-root. |
securityContext.allowPrivilegeEscalation |
boolean | false |
Disable privilege escalation. |
securityContext.readOnlyRootFilesystem |
boolean | false |
Keep the root filesystem writable for PostgreSQL runtime needs. |
securityContext.capabilities.drop |
array | ["ALL"] |
Linux capabilities dropped from PostgreSQL containers. |
podLabels |
object | {} |
Extra labels applied to PostgreSQL pods. |
podAnnotations |
object | {} |
Extra annotations applied to PostgreSQL pods. |
annotations |
object | {} |
Deprecated alias for commonAnnotations. |
nodeSelector |
object | {} |
Node selector applied to PostgreSQL pods. |
tolerations |
array | [] |
Tolerations applied to PostgreSQL pods. |
affinity |
object | {} |
Explicit affinity rules. |
topologySpreadConstraints |
array | [] |
Explicit topology spread constraints. |
priorityClassName |
string | "" |
PriorityClass applied to PostgreSQL pods. |
terminationGracePeriodSeconds |
integer | 120 |
Termination grace period for PostgreSQL pods. |
Extra
| Parameter | Type | Default | Description |
|---|---|---|---|
extraEnv |
array | [] |
Extra environment variables injected into PostgreSQL containers. |
extraVolumes |
array | [] |
Extra volumes appended to PostgreSQL pods. |
extraVolumeMounts |
array | [] |
Extra volume mounts appended to PostgreSQL containers. |
External Secrets Operator
| Parameter | Type | Default | Description |
|---|---|---|---|
externalSecrets.enabled |
boolean | false |
Render ExternalSecret resources. |
externalSecrets.apiVersion |
string | external-secrets.io/v1 |
ExternalSecret API version. |
externalSecrets.refreshInterval |
string | 1h |
Refresh interval used by rendered ExternalSecrets. |
externalSecrets.secretStoreRef.name |
string | "" |
Existing SecretStore or ClusterSecretStore name. |
externalSecrets.auth.enabled |
boolean | false |
Manage the PostgreSQL auth Secret through ESO. |
externalSecrets.tls.enabled |
boolean | false |
Manage the TLS Secret through ESO. |
externalSecrets.backup.enabled |
boolean | false |
Manage the backup S3 credential Secret through ESO. |
Upgrade Notes
Preserve the values file used for the release and upgrade it explicitly:
helm upgrade my-pg helmforge/postgresql -n <namespace> -f values.yaml
kubectl rollout status statefulset/my-pg-postgresql -n <namespace>
Existing StatefulSets, Services, Secrets, and PVCs retain their Kubernetes identity when resource names are unchanged.
Generated passwords are looked up and reused during live Helm upgrades. Do not use --reset-values unless returning to
chart defaults is intentional, and never change the image major version without a PostgreSQL migration plan.
PostgreSQL 18.6 is the August 2026 security and bug-fix release; upstream did not publish 18.5 because of a post-wrap regression. Back up the cluster and review the official PostgreSQL 18.6 release notes before upgrading. A dump and restore is not required from an earlier PostgreSQL 18 release, but test extensions, authentication, replication, and application queries in staging.
If upgrading across major versions, check the
changelog
for breaking changes in values structure. Always back up your data before upgrading.
- When switching from
standalonetoreplication, setauth.replicationPasswordbefore upgrading - PVC resize requires the storage class to support volume expansion (
allowVolumeExpansion: true) - Metrics sidecar changes may cause a pod restart during upgrade
Common Issues
Usually caused by incorrect auth.postgresPassword on upgrade. PostgreSQL does not re-initialize
auth when the data directory already exists. Check logs with kubectl logs <pod> and verify the
password matches the existing data.
For large databases, initial replica sync can take time. Increase readReplicas.resources and ensure the storage
class provides adequate IOPS. Monitor replication lag with the Prometheus exporter (pg_replication_lag metric).
For high-connection workloads, deploy PgBouncer alongside PostgreSQL. Point your application to PgBouncer and configure it to connect to the PostgreSQL service.