Mounting the CA bundle
Tested with: cert-manager 1.16 · trust-manager 0.12 · Kubernetes 1.31
The gateway’s certificate is publicly trusted, so Go, Node, .NET, JVM and most Ruby drivers verify it from the OS
bundle in the container image. libpq-based drivers (psql, psycopg, Ruby pg, PHP PDO pgsql, Npgsql with
Root Certificate) and MySQL clients with VERIFY_IDENTITY need a file path. Mount it; do not bake it into images,
because the bundle rotates.
zb api GET /v1/orgs/{org}/ca.pem > databasezy-ca.pemkubectl -n app create configmap databasezy-ca --from-file=ca.crt=databasezy-ca.pem \ --dry-run=client -o yaml | kubectl apply -f -env: - name: PGSSLMODE value: verify-full - name: PGSSLROOTCERT value: /etc/databasezy/ca.crtvolumeMounts: - { name: databasezy-ca, mountPath: /etc/databasezy, readOnly: true }volumes: - name: databasezy-ca configMap: { name: databasezy-ca }trust-manager (part of cert-manager) copies a bundle into every namespace you label, so teams never handle the file. Publish the source once in the trust namespace:
zb api GET /v1/orgs/{org}/ca.pem > databasezy-ca.pemkubectl -n cert-manager create configmap databasezy-ca-source --from-file=ca.crt=databasezy-ca.pem \ --dry-run=client -o yaml | kubectl apply -f -apiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata: name: databasezy-caspec: sources: - configMap: name: databasezy-ca-source key: ca.crt - useDefaultCAs: true # keep the public roots too target: configMap: key: ca-bundle.crt namespaceSelector: matchLabels: { databasezy.com/trust: "true" }kubectl label namespace app databasezy.com/trust=truePods then mount the databasezy-ca ConfigMap at /etc/ssl/certs/databasezy (or point SSL_CERT_FILE at it for
OpenSSL-based clients).
JDBC drivers for MySQL, Valkey (Lettuce) and MongoDB want a PKCS12 truststore. Build it at pod start so rotation does not require an image rebuild:
initContainers: - name: truststore image: eclipse-temurin:21-jre command: ["sh", "-c"] args: - keytool -importcert -noprompt -alias databasezy -file /ca/ca.crt -keystore /truststore/databasezy.p12 -storetype PKCS12 -storepass changeit volumeMounts: - { name: databasezy-ca, mountPath: /ca, readOnly: true } - { name: truststore, mountPath: /truststore }containers: - name: app env: - name: JAVA_TOOL_OPTIONS value: "-Djavax.net.ssl.trustStore=/truststore/databasezy.p12 -Djavax.net.ssl.trustStorePassword=changeit" volumeMounts: - { name: truststore, mountPath: /truststore, readOnly: true }volumes: - name: databasezy-ca configMap: { name: databasezy-ca } - name: truststore emptyDir: {}Rotation
Section titled “Rotation”The intermediate rotates on the dates published under Settings → Certificates; we email org admins 30 days
before. The published bundle always contains both the current and the next intermediate, so refreshing it once in
that window is enough. A CronJob that fetches the bundle with zb api and updates the ConfigMap keeps everything current:
apiVersion: batch/v1kind: CronJobmetadata: name: refresh-databasezy-ca namespace: cert-managerspec: schedule: "0 3 * * 1" jobTemplate: spec: template: spec: serviceAccountName: refresh-databasezy-ca # RBAC: get/update configmaps in this namespace restartPolicy: OnFailure containers: - name: refresh image: ghcr.io/zerobase/zb:0.1 envFrom: - secretRef: { name: databasezy-api } # ZB_API_KEY and ZB_ORG command: ["sh", "-c"] args: - zb api GET '/v1/orgs/{org}/ca.pem' > /tmp/ca.crt && kubectl -n cert-manager create configmap databasezy-ca-source --from-file=ca.crt=/tmp/ca.crt --dry-run=client -o yaml | kubectl apply -f -