Deploy Pulse databases with Helm
Use the umbrella chart to deploy Pulse database dependencies on Kubernetes by using supported operators and charts.helm-databases
Supported databases
Database | Operator or chart | Version |
MongoDB | MongoDB Controllers for Kubernetes (MCK) | 1.7.0 |
PostgreSQL | CloudNativePG | 0.27.1 |
Elasticsearch | ECK Operator | 3.3.0 |
VictoriaMetrics | VictoriaMetrics Operator | 0.58.1 |
Redis | OpsTree Redis Operator | 0.23.0 |
NATS | NATS Helm Chart | 1.3.16 |
Before You Begin
Make sure you have:
- Kubernetes 1.21 or later
- Helm 3.x
configured for your clusterkubectl- Cluster admin permissions to install CRDs and ClusterRoles
Add Helm repositories
Before installing, you must register the Helm repositories that this chart references. Run all six commands, or only the ones for the databases you plan to install:
helm repo add mongodb https://mongodb.github.io/helm-charts
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo add elastic https://helm.elastic.co
helm repo add victoriametrics https://victoriametrics.github.io/helm-charts/
helm repo add ot-container-kit https://ot-container-kit.github.io/helm-charts
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm repo update
Then pull the dependency charts:
helm dependency update ./helm-databases
Installation Modes
This chart supports two install modes using the same chart.
Fresh Install: Two-Step Process
On a fresh cluster where operator CRDs do not exist yet, Helm cannot create custom resources (database instances) in the same transaction that installs the CRDs. The chart handles this automatically.
Step 1 -- Install operators + CRDs (database instances are auto-deferred)
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace
Step 2 -- Create database instances (CRDs now exist in the API)
helm upgrade pulse-db ./helm-databases \
--namespace pulse-databases \
--reuse-values
The post-install notes will tell you when step 2 is needed. On subsequent upgrades, everything works in a single command.
Mode 1: All databases in a single namespace
Deploy every enabled database into one shared namespace. Follow the two-step process above on a fresh cluster.
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace
Disable specific databases at install time:
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set nats.enabled=false \
--set victoriametrics.enabled=false
Mode 2: Each database in its own namespace
Use the convenience value files in to install one database per release. The same two-step process applies per release.values/
MongoDB
helm install pulse-mongo ./helm-databases \
-n pulse-mongo --create-namespace \
-f helm-databases/values/mongo-only.yaml
PostgreSQL
helm install pulse-pg ./helm-databases \
-n pulse-postgres --create-namespace \
-f helm-databases/values/postgres-only.yaml
Elasticsearch
helm install pulse-es ./helm-databases \
-n pulse-elastic --create-namespace \
-f helm-databases/values/elastic-only.yaml
VictoriaMetrics
helm install pulse-vm ./helm-databases \
-n pulse-victoriametrics --create-namespace \
-f helm-databases/values/victoriametrics-only.yaml
Redis
helm install pulse-redis ./helm-databases \
-n pulse-redis --create-namespace \
-f helm-databases/values/redis-only.yaml
NATS
helm install pulse-nats ./helm-databases \
-n pulse-nats --create-namespace \
-f helm-databases/values/nats-only.yaml
MongoDB Sharded Cluster
The chart supports deploying a sharded MongoDB cluster by using the Community Operator. No Ops Manager is required. Set to enable it.mongodb.cr.type=shardedCluster
Info
The operator does not officially support sharding. It works by deploying separate MongoDBCommunity CRs for config servers, shard servers, and MongoDBCommunity routers, wired together through a shared keyfile and a mongos command override. Test thoroughly before production use.mongos
What Gets Created
When is set, the chart creates:type: shardedCluster
Resource | Name | Description |
Secret | | Master keyfile (auto-generated or user-provided) |
Secret (×N) |
| Per-component copies of the keyfile |
Secret | admin password secret | Shared admin credentials |
MongoDBCommunity | | Config server replica set ( |
MongoDBCommunity (×shardCount) |
| Shard replica sets ( |
MongoDBCommunity | |
|
Job (Helm hook) | | Post-install job that runs |
Deploy
Step 1 -- Install the operator (same two-step process as a fresh install)
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set mongodb.cr.type=shardedCluster
Step 2 -- Create the sharded cluster CRs
helm upgrade pulse-db ./helm-databases \
--namespace pulse-databases \
--reuse-values
Deploy with Custom Settings
Override shard count, router count, or provide your own keyfile:
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set mongodb.cr.type=shardedCluster \
--set mongodb.cr.shardedCluster.shardCount=3 \
--set mongodb.cr.shardedCluster.membersPerShard=3 \
--set mongodb.cr.shardedCluster.mongosCount=3 \
--set mongodb.cr.shardedCluster.configServerCount=3 \
--set mongodb.cr.auth.adminPassword=mySecurePassword
To supply your own keyfile (must be at least 6 characters):
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set mongodb.cr.type=shardedCluster \
--set mongodb.cr.shardedCluster.keyfile.value="$(openssl rand -base64 756)"
If is left empty, which is the default, a random keyfile is auto-generated on first install and preserved across upgrades.keyfile.value
Verify the Cluster
After the install and upgrade cycle, check the status of all components:
kubectl get mongodbcommunity -n pulse-databases
kubectl get job pulse-mongodb-add-shards -n pulse-databases
kubectl logs job/pulse-mongodb-add-shards -n pulse-databases
kubectl exec -it pulse-mongodb-mongos-0 -n pulse-databases -- \
mongosh --eval "sh.status()"
Architecture Overview
┌──────────────────────────────────────────────────────────┐
│ Client Applications │
└───────────────────────────┬──────────────────────────────┘
│
┌─────────────▼─────────────┐
│ pulse-mongodb-mongos │
│ (mongos query routers) │
│ N = mongosCount │
└──┬──────────────────────┬──┘
│ │
┌────────────▼──────────┐ ┌───────▼────────────────┐
│ pulse-mongodb-shard-0 │ │ pulse-mongodb-shard-N │
│ (shardsvr replset) │ │ (shardsvr replset) │
│ M = membersPerShard │ │ M = membersPerShard │
└───────────────────────┘ └─────────────────────────┘
│ │
┌───────────────────────────────────┐
│ pulse-mongodb-configsvr │
│ (configsvr replica set) │
│ N = configServerCount │
└───────────────────────────────────┘
All components share a common keyfile for internal authentication.
Using a Self-Hosted / Mirror Chart Repository
The references repositories by using Helm aliases, such as Chart.yaml. To point at your own mirror, re-add the alias before running @mongodb.helm dependency update
helm repo add mongodb https://my-internal-registry.example.com/mongodb-charts
helm repo add elastic https://my-internal-registry.example.com/elastic-charts
helm dependency update ./helm-databases
Each repository alias can be overridden independently, which gives you per-database control over the chart source.
Configuration
Global Values
Parameter | Description | Default |
| Image pull secrets for operator pods | |
| Enable OpenShift-compatible security contexts | |
Per-Database Values
Each database section in has the same structure:values.yaml
<database>:
enabled: true
<operator-alias>: {}
cr:
create: true
name: pulse-<database>
...
MongoDB
Parameter | Description | Default |
| Deploy MCK operator + instance | |
| Create MongoDB CR(s) | |
| Instance name (base name for sharded components) | |
|
| |
| MongoDB version | |
| Replica set members ( | |
| Number of shard replica sets | |
| Members per shard replica set | |
|
| |
| Config server replica set members | |
| Custom keyfile string (auto-generated if empty) | |
| Data volume size | |
| Default admin username | |
| Default admin password (CHANGE THIS) | |
PostgreSQL (CloudNativePG)
Parameter | Description | Default |
| Deploy CNPG operator + cluster | |
| Create Cluster CR | |
| Cluster name | |
| Number of instances | |
| PostgreSQL major version | |
| Data volume size | |
| Initial database name | |
| Database owner | |
Elasticsearch (ECK)
Parameter | Description | Default |
| Deploy ECK operator + cluster | |
| Create Elasticsearch CR | |
| Cluster name | |
| Elasticsearch version | |
| Node set definitions (count, storage, resources) | 1 set, 3 nodes, 20Gi |
VictoriaMetrics
Parameter | Description | Default |
| Deploy VM operator + instance | |
| Create VMSingle CR | |
| Instance name | |
| Data retention period | |
| Data volume size | |
Redis (OpsTree)
Parameter | Description | Default |
| Deploy Redis operator + instance | |
| Create Redis CR | |
| Instance name | |
| Deployment mode: | |
| Nodes (for cluster, replication, sentinel modes) | |
| Redis image tag | |
| Data volume size | |
| Enable Prometheus exporter sidecar | |
NATS
| Deploy NATS | |
| List of image pull secret names for private registries | |
| Custom NATS server image repository | |
| Custom NATS server image tag | |
| Custom image registry (overrides Docker Hub) | |
| Full image name (overrides registry + repo + tag) | |
| Deploy | |
| Enable NATS clustering | |
| Cluster replicas | |
| Enable JetStream persistence | |
| JetStream PVC size | |
For full NATS chart values, see the official NATS chart docs.
Operator-Only Install
To deploy only the operators without creating database instances, which is useful for shared operator installations, set :cr.create: false
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set mongodb.cr.create=false \
--set postgresql.cr.create=false \
--set elasticsearch.cr.create=false \
--set victoriametrics.cr.create=false \
--set redis.cr.create=false
You can then create database instances separately by applying CRs with .kubectl
Skipping CRD Installation
If CRDs already exist in the cluster, from a prior install or another release, you may get ownership conflicts. Use these options to skip CRD installation:
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--skip-crds \
--set postgresql.crds.create=false \
--set elasticsearch.installCRDs=false \
--set victoriametrics.crds.enabled=false
Database | How to skip CRDs |
MongoDB |
|
PostgreSQL | |
Elasticsearch | |
VictoriaMetrics | |
Redis |
|
NATS | No CRDs |
Alternatively, adopt existing CRDs into your release:
kubectl label crd mongodbcommunity.mongodbcommunity.mongodb.com \
app.kubernetes.io/managed-by=Helm
kubectl annotate crd mongodbcommunity.mongodbcommunity.mongodb.com \
meta.helm.sh/release-name=<RELEASE_NAME> \
meta.helm.sh/release-namespace=<NAMESPACE>
OpenShift Support
Set to enable OpenShift-compatible settings:global.openshift=true
helm install pulse-db ./helm-databases \
--namespace pulse-databases \
--create-namespace \
--set global.openshift=true \
--set postgresql.containerSecurityContext.runAsUser=null \
--set postgresql.containerSecurityContext.runAsGroup=null
This will:
- Remove fixed
/runAsUsersettings from all CR pod templates (MongoDB, Elasticsearch, Redis, VictoriaMetrics, PostgreSQL)runAsGroup - Let OpenShift Security Context Constraints (SCCs) assign UIDs
- Avoid
in all workloadsprivileged: true
Built-in OpenShift support per operator
Operator | OpenShift handling |
ECK | Auto-detects OpenShift via |
VictoriaMetrics | Auto-adapts via |
OpsTree Redis | Built-in OpenShift support ( |
CloudNativePG | Set |
MongoDB | CR template clears |
NATS | Standard |
Upgrading
helm dependency update ./helm-databases
helm upgrade pulse-db ./helm-databases \
--namespace pulse-databases
Uninstalling
helm uninstall pulse-db --namespace pulse-databases
CRDs are not automatically removed by Helm.
To remove CRDs manually, which deletes all custom resources, run:
kubectl get crd | grep -E 'mongodb|cnpg|elastic|victoriametrics|redis.opstreelabs' | awk '{print $1}' | xargs kubectl delete crd
Commands to Install Databases on OpenShift
MongoDB
helm install pulse-mongo ./helm-databases \
-n pulse-mongo --create-namespace \
-f helm-databases/values/mongo-only.yaml \
--set mongodb.cr.version="8.0.18" \
--set mongodb.cr.mongodImage="191579300362.dkr.ecr.us-east-1.amazonaws.com/acceldata/ad-database:testBuild-k8sqe" \
--set global.openshift=true \
--set mongodb.managedSecurityContext=true \
--set global.imagePullSecrets[0]=adreg \
--set mongodb.registry.imagePullSecrets=adreg \
--set global.imagePullPolicy=IfNotPresent \
--set global.storageClassName='gp2-csi' \
--set global.imageCredentials.password='<REDACTED_EXISTING_SECRET>' \
--set mongodb.cr.storage.size=50Gi \
--set mongodb.cr.resources.limits.cpu=1 \
--set mongodb.cr.resources.limits.memory=4Gi \
--set mongodb.cr.resources.requests.cpu=500m \
--set mongodb.cr.resources.requests.memory=2Gi \
--timeout 20m
VictoriaMetrics
helm install pulse-vm ./helm-databases \
-n pulse-vm --create-namespace \
--set victoriametrics.cr.auth.username=acceldata \
--set 'victoriametrics.cr.auth.password=D@t@Ops' \
--set mongodb.managedSecurityContext=true \
--set global.openshift=true \
--set nats.global.image.pullSecretNames[0]=adreg \
--set global.storageClassName='gp2-csi' \
--set global.imageCredentials.password='<REDACTED_EXISTING_SECRET>' \
--set victoriametrics.cr.vmstorage.storage.size=50Gi \
--set victoriametrics.cr.vmstorage.resources.limits.cpu=1 \
--set victoriametrics.cr.vmstorage.resources.limits.memory=4Gi \
--set victoriametrics.cr.vmstorage.resources.requests.cpu=500m \
--set victoriametrics.cr.vmstorage.resources.requests.memory=1Gi \
--set victoriametrics.cr.vmselect.storage.size=10Gi \
--set victoriametrics.cr.vmselect.resources.limits.cpu=1 \
--set victoriametrics.cr.vmselect.resources.limits.memory=2Gi \
--set victoriametrics.cr.vmselect.resources.requests.cpu=500m \
--set victoriametrics.cr.vmselect.resources.requests.memory=512Mi \
--set victoriametrics.cr.vminsert.resources.limits.cpu=1 \
--set victoriametrics.cr.vminsert.resources.limits.memory=2Gi \
--set victoriametrics.cr.vminsert.resources.requests.cpu=500m \
--set victoriametrics.cr.vminsert.resources.requests.memory=512Mi \
-f helm-databases/values/victoriametrics-only.yaml
helm upgrade pulse-vm ./helm-databases \
--namespace pulse-vm \
--reuse-values
Elastic
helm install pulse-es ./helm-databases \
-n pulse-es --create-namespace \
--set global.openshift=true \
--set mongodb.managedSecurityContext=true \
--set elasticsearch.cr.auth.password="admin_password" \
--set elasticsearch.cr.auth.additionalUsers[0].username=pulse \
--set elasticsearch.cr.auth.additionalUsers[0].password="data@ops" \
--set elasticsearch.cr.auth.additionalUsers[0].roles[0]=superuser \
--set elasticsearch.cr.tls.p12.password="" \
--set elasticsearch.cr.httpPort=19013 \
--set elasticsearch.cr.tls.disabled=true \
--set nats.global.image.pullSecretNames[1]=adreg \
--set global.storageClassName='gp2-csi' \
--set global.imageCredentials.password='<REDACTED_EXISTING_SECRET>' \
--set elasticsearch.cr.nodeSets[0].name=default \
--set elasticsearch.cr.nodeSets[0].count=3 \
--set elasticsearch.cr.nodeSets[0].config.node\\.store\\.allow_mmap=true \
--set elasticsearch.cr.nodeSets[0].storage.size=50Gi \
--set elasticsearch.cr.nodeSets[0].resources.limits.cpu=1 \
--set elasticsearch.cr.nodeSets[0].resources.limits.memory=8Gi \
--set elasticsearch.cr.nodeSets[0].resources.requests.cpu=500m \
--set elasticsearch.cr.nodeSets[0].resources.requests.memory=4Gi \
-f helm-databases/values/elastic-only.yaml
helm upgrade pulse-es ./helm-databases \
--namespace pulse-es \
--reuse-values
kubectl port-forward svc/pulse-elasticsearch-es-http -n pulse-es 19013:19013
curl -s -u "pulse:data@ops" \
-X PUT "http://localhost:19013/_index_template/pulse-defaults" \
-H 'Content-Type: application/json' \
-d '{
"index_patterns": ["*"],
"priority": 1,
"template": {
"settings": {
"number_of_replicas": 1
}
}
}'
Postgres
helm install pulse-pg ./helm-databases \
-n pulse-pg --create-namespace \
-f helm-databases/values/postgres-only.yaml \
--set postgresql.useOperator=false \
--set nats.global.image.pullSecretNames[0]=adreg \
--set global.storageClassName='gp2-csi' \
--set global.imageCredentials.password='<REDACTED_EXISTING_SECRET>' \
--set postgresql.standalone.storage.size=50Gi \
--set postgresql.standalone.resources.limits.cpu=1 \
--set postgresql.standalone.resources.limits.memory=4Gi \
--set postgresql.standalone.resources.requests.cpu=500m \
--set postgresql.standalone.resources.requests.memory=2Gi \
-f helm-databases/values/openshift.yaml
NATS
helm install pulse-nats ./helm-databases \
-n pulse-nats --create-namespace \
--set global.openshift=true \
-f helm-databases/values/nats-only.yaml \
--set nats.global.image.pullSecretNames[0]=adreg \
--set nats.loadBalancer.enabled=true \
--set global.storageClassName='gp2-csi' \
--set nats.config.jetstream.fileStore.pvc.storageClassName='gp2-csi' \
--set global.imageCredentials.password='<REDACTED_EXISTING_SECRET>' \
--set nats.config.jetstream.fileStore.pvc.size=20Gi \
--set nats.container.merge.resources.limits.cpu=1 \
--set nats.container.merge.resources.limits.memory=4Gi \
--set nats.container.merge.resources.requests.cpu=500m \
--set nats.container.merge.resources.requests.memory=2Gi

Have a suggestion?