Skip to content

High Availability with PostgreSQL

QHx supports running SPIRE Server in high-availability (HA) mode with multiple replicas sharing an external PostgreSQL datastore. This guide covers both in-cluster PostgreSQL operators and managed services such as AWS RDS.

  • QHx installed in your cluster via Helm (see Helm-Based Installation)
  • A StorageClass that supports ReadWriteOnce for SPIRE Server PVCs
  • kubectl configured for your cluster
  • Customer credentials for oci.messier42.com

Amazon RDS for PostgreSQL is a managed option that removes the operational burden of running PostgreSQL inside the cluster.

Create the RDS instance (or use an existing one) and note the endpoint, e.g. spire.cxxx.us-east-1.rds.amazonaws.com.

Create the spire database and user:

CREATE DATABASE spire;
CREATE USER spire WITH PASSWORD '<strong-password>';
GRANT ALL PRIVILEGES ON DATABASE spire TO spire;

Ensure the RDS security group allows inbound TCP on port 5432 from the CIDR or security group of your cluster nodes.

The DSN to use in Step 2:

postgresql://spire:<password>@spire.cxxx.us-east-1.rds.amazonaws.com:5432/spire?sslmode=require

RDS uses a certificate from Amazon’s CA. If your cluster doesn’t have that CA in its trust store, add sslrootcert=/path/to/aws-rds-ca.pem to the DSN, or use sslmode=require without certificate verification (acceptable for most in-cluster use cases):

postgresql://spire:<password>@spire.cxxx.us-east-1.rds.amazonaws.com:5432/spire?sslmode=require

You do not need to deploy a PostgreSQL operator — skip straight to Step 2 once the RDS instance is ready.


SPIRE Server connects to PostgreSQL using the pkiDatastoreConnectionString field. Because this value contains credentials it must be stored in the manager Secret, not the ConfigMap.

The Secret must use the key qhx-manager-secret.yaml and contain valid YAML:

qhx-manager-secret.yaml (local file — do not commit)
pkiDatastoreConnectionString: "postgresql://spire:<password>@spire-pg-rw.qhx-system.svc.cluster.local:5432/spire?sslmode=require"

Create or update the Secret in the cluster:

Terminal window
kubectl create secret generic qhx-manager-secret \
--namespace qhx-system \
--from-file=qhx-manager-secret.yaml=./qhx-manager-secret.yaml \
--dry-run=client -o yaml | kubectl apply -f -

Step 3 — Configure the Manager Secret Reference

Section titled “Step 3 — Configure the Manager Secret Reference”

If you did not set managerSecretName during the initial install, upgrade the release to add it now. Use the same version and credentials you used when installing QHx:

Upgrade QHx to enable the manager Secret
$ helm upgrade qhx oci://oci.messier42.com/qhx/charts/qhx-core \
--version <INSTALLED-VERSION> \
--namespace qhx-system \
--reuse-values \
--set managerSecretName=qhx-manager-secret \
--set managerSecretNamespace=qhx-system \
--wait

Edit the qhx-manager ConfigMap to enable multiple SPIRE Server replicas and configure storage for the per-replica PVCs:

Terminal window
kubectl edit configmap qhx-manager -n qhx-system

Add or update these fields:

pkiServerReplicas: "2" # number of SPIRE Server replicas (≥ 2 for HA)
pkiStorageClassName: "standard" # StorageClass that supports ReadWriteOnce
pkiStorageSize: "1Gi" # size of each replica's PVC

The manager watches the ConfigMap and picks up changes without a restart. It will trigger a rolling update of the SPIRE Server StatefulSet automatically.


Check that all SPIRE Server pods are running:

Terminal window
kubectl get pods -n qhx-system -l app.kubernetes.io/component=pki-server

Expected output (2-replica example):

NAME READY STATUS RESTARTS AGE
qhx-spire-server-0 1/1 Running 0 2m
qhx-spire-server-1 1/1 Running 0 90s

Verify that one PVC was created per replica:

Terminal window
kubectl get pvc -n qhx-system -l app.kubernetes.io/component=pki-server

Confirm that SPIRE Server logs show the PostgreSQL datastore plugin:

Terminal window
kubectl logs -n qhx-system qhx-spire-server-0 | grep -i datastore

You should see lines similar to:

INFO Datastore connected subsystem_name=catalog type=sql driver=postgres

AspectDetail
Pod placementThe manager configures a soft (PreferredDuringScheduling) anti-affinity rule so replicas prefer different nodes.
Startup orderParallelPodManagement is enabled — all pods start simultaneously rather than one-by-one.
Leader electionSPIRE Server leader election is automatically enabled when pkiServerReplicas > 1.
PVC lifecycleEach replica gets its own PVC via VolumeClaimTemplates. Scaling down does not delete PVCs automatically.
ConfigMap / Secret reloadBoth are watched live; no manager restart is required when you change the DSN or replica count.
StatefulSet VolumeClaimTemplatesThese are immutable in Kubernetes. If you need to change pkiStorageClassName or pkiStorageSize after the StatefulSet exists, the manager will delete and recreate it automatically.

To disable HA and revert to the embedded SQLite datastore:

  1. Set pkiServerReplicas: "1" in the ConfigMap.
  2. Remove or clear pkiDatastoreConnectionString from the Secret.
  3. The manager will update the StatefulSet; the unused PVCs can be deleted manually.