A production-ready Helm chart for deploying OpenBao vault on Kubernetes with dedicated vault infrastructure, NFS persistent storage, and secure cluster-only access.
- Dedicated Vault Nodes: Deploy vault on dedicated Kubernetes nodes with taints and node affinity
- Persistent NFS Storage: Secure data persistence usi# Test template rendering
helm template test-vault charts/openbao-vault
-f charts/openbao-vault/values/openbao-vault.yaml
helm install --dry-run test-release charts/openbao-vault
-f charts/openbao-vault/values/openbao-vault.yamlshared storage
- Security-First: Internal ClusterIP services with no external exposure by default
- Production Ready: Health checks, resource limits, and proper RBAC
- High Availability: Supports multi-node vault configurations
- Kubernetes Native: Designed specifically for Kubernetes environments
- Kubernetes cluster (1.19+)
- Helm 3.x
- StorageClass for dynamic provisioning (e.g.,
do-block-storagefor DigitalOcean) - kubectl configured with cluster access
For optimal security and performance, this chart supports dedicated nodes for vault deployment. Dedicated nodes prevent resource contention and provide better isolation for sensitive vault operations.
For DigitalOcean Kubernetes (DOKS):
# Create a new node pool for vault
doctl kubernetes cluster node-pool create <cluster-name> \
--name vault-nodes \
--size s-4vcpu-8gb \
--count 1 \
--tag vault,secure
# Or label existing nodes
kubectl label node <node-name> node-role.kubernetes.io/vault=trueFor other Kubernetes platforms:
# Label a node for vault deployment
kubectl label node <node-name> node-role.kubernetes.io/vault=trueTaint nodes to ensure only vault workloads are scheduled:
# Taint nodes to dedicate them for vault workloads
kubectl taint node <vault-node> node-role.kubernetes.io/vault=true:NoSchedule# Check node labels and taints
kubectl get nodes --show-labels
kubectl describe node <node-name>The vault configuration expects specific node labels:
| Component | Node Label | Recommended Node Size |
|---|---|---|
| OpenBao Vault | node-role.kubernetes.io/vault=true |
4-8 CPU, 8-16GB RAM |
Deploy the vault with dedicated configuration:
# Deploy OpenBao Vault
helm install openbao-vault ./charts/openbao-vault \
--namespace openbao-vault \
--values ./charts/openbao-vault/values/openbao-vault.yaml \
--create-namespace# Check helm release
helm list --all-namespaces
# Monitor pod status
kubectl get pods -n openbao-vault
# Check services (note: ClusterIP only for security)
kubectl get svc -n openbao-vaultThe vault is configured for internal cluster access only:
# Port-forward to access vault locally
kubectl port-forward -n openbao-vault svc/openbao-vault 8200:8200
# Access vault at http://localhost:8200
# Use the vault CLI or web interface# Initialize the vault (first time only)
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator init
# Unseal the vault (use keys from init output)
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator unseal <unseal-key-1>
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator unseal <unseal-key-2>
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator unseal <unseal-key-3># Check vault status
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao status
# Or via port-forward and HTTP API
curl -s http://localhost:8200/v1/sys/health | jqThe chart includes pre-configured values for different deployment scenarios:
Vault Deployment:
openbao-vault.yaml- Production vault on vault nodes with security hardening
You can override any values by creating your own values file or using --set flags:
helm install my-vault ./charts/openbao-vault \
-f ./charts/openbao-vault/values/openbao-vault.yaml \
--set vault.resources.limits.memory=16Gi \
--set nfs.storage.size=50Gi- Node Selection: Configure in
nodeSelectorandtolerations - Resource Limits: Configure CPU/memory in
vault.resourcesandnfs.resources - Storage Size: Adjust
nfs.storage.sizebased on vault data requirements - Security: All services use ClusterIP for internal-only access
- Namespace: Configurable namespace for deployment isolation
Each deployment includes:
- Namespace: Isolated environment for vault deployment (
openbao-vault) - NFS Server: Persistent storage for vault data using
boyroywax/nfs-server:1.0.0 - OpenBao Vault: Main vault service using
openbao/openbao:latest(v2.4.1) - RBAC: Proper ServiceAccount, ClusterRole, and ClusterRoleBinding for security
- Services: Internal ClusterIP services only (no external exposure)
- ConfigMaps: Configuration management for vault and NFS
openbao-helm-chart/
βββ README.md # This file - comprehensive documentation
βββ CHANGELOG.md # Version history and release notes
βββ CONTRIBUTING.md # Contribution guidelines
βββ LICENSE # MIT license
βββ create-docker-secret.sh # Helper script for Docker registry secrets
βββ .gitignore # Git ignore rules
βββ charts/
βββ openbao-vault/ # Main Helm chart
βββ Chart.yaml # Chart metadata and version info
βββ values.yaml # Default values
βββ .helmignore # Helm ignore rules
βββ values/
β βββ openbao-vault-dedicated.yaml # Production values for dedicated deployment
βββ templates/
βββ _helpers.tpl # Template helpers and functions
βββ namespace.yaml # Namespace creation
βββ pv-creator-rbac.yaml # RBAC for PV management
βββ nfs-pv-creator.yaml # Job to create NFS PersistentVolume
βββ nfs-server.yaml # NFS server deployment and service
βββ storage.yaml # PVC for vault data
βββ deployment.yaml # Main vault deployment and service
βββ vault-init-job.yaml # Optional vault initialization job
βββ ingress.yaml # Ingress (disabled by default)
Symptoms: Pods show Pending status
kubectl get pods -n openbao-vault
# NAME READY STATUS RESTARTS AGE
# openbao-vault-xxx 0/1 Pending 0 5mDiagnosis:
kubectl describe pod -n openbao-vault <pod-name>Common Causes & Solutions:
a) Node Taint Issues
# Error: "0/5 nodes are available: 2 node(s) had untolerated taint {node-role.kubernetes.io/vault: true}"
# Solution: Ensure your values file has proper tolerations:
tolerations:
- key: "node-role.kubernetes.io/vault"
operator: "Equal"
value: "true"
effect: "NoSchedule"b) PVC Binding Issues
# Error: "pod has unbound immediate PersistentVolumeClaims"
# Check PVC status
kubectl get pvc -n openbao-vault
# Check NFS server status
kubectl logs -n openbao-vault deployment/openbao-vault-nfs-serverc) Resource Constraints
# Error: "Insufficient cpu" or "Insufficient memory"
# Check node resources
kubectl describe node <vault-node>
# Scale up your cluster or reduce resource requestsSymptoms: Vault shows as sealed or uninitialized
kubectl exec -n openbao-vault -it deployment/openbao-vault -- bao status
# Error: Vault is sealedSolutions:
# Initialize vault if not done yet
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator init
# Unseal vault with keys from initialization
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator unseal <key>If pods are stuck with PVC binding issues:
# Check if NFS server is running
kubectl get pods -n openbao-vault | grep nfs-server
# Check NFS server logs
kubectl logs -n openbao-vault deployment/openbao-vault-nfs-server
# Test NFS mount manually
kubectl run nfs-test --image=busybox --rm -it --restart=Never \
--overrides='{
"spec": {
"containers": [{
"name": "nfs-test",
"image": "busybox",
"command": ["/bin/sh", "-c", "mount -t nfs <nfs-server-ip>:/nfsshare/data /mnt && ls -la /mnt"],
"volumeMounts": [{"name": "nfs-test", "mountPath": "/mnt"}]
}],
"volumes": [{"name": "nfs-test", "nfs": {"server": "<nfs-server-ip>", "path": "/nfsshare/data"}}]
}
}' -n openbao-vault -- /bin/sh
# Check persistent volumes
kubectl get pv | grep openbao-vault
kubectl describe pv openbao-vault-nfs-pvCommon NFS Issues:
- NFS server startup: The vault pod depends on NFS server being ready
- Network policies: Ensure NFS traffic (ports 2049, 111, 20048) is allowed
- Storage class: Verify the NFS server's backend storage is properly provisioned
# List vault deployment
helm list --all-namespaces | grep openbao
# Check specific deployment
helm status openbao-vault -n openbao-vault
# View all pods
kubectl get pods -n openbao-vault -o wide# Vault service logs
kubectl logs -n openbao-vault deployment/openbao-vault
# NFS server logs
kubectl logs -n openbao-vault deployment/openbao-vault-nfs-server
# Get vault status
kubectl exec -n openbao-vault -it deployment/openbao-vault -- bao status# Create a backup (vault must be unsealed)
kubectl exec -n openbao-vault -it deployment/openbao-vault -- \
bao operator raft snapshot save backup.snap
# Copy backup out of pod
kubectl cp openbao-vault/openbao-vault-xxx:/backup.snap ./vault-backup.snap- Internal Only: All services use ClusterIP (no external exposure)
- Dedicated Nodes: Vault runs on dedicated, tainted nodes
- RBAC: Minimal required permissions via ServiceAccount
- Network Policies: Consider implementing network policies for additional security
- TLS: Configure TLS certificates for production deployments
- Enable TLS: Configure TLS certificates for vault API
- Network Policies: Restrict pod-to-pod communication
- Sealed Secrets: Use sealed-secrets or external secret management
- Audit Logging: Enable vault audit logging
- Backup Encryption: Encrypt vault snapshots
# Validate chart syntax
helm lint charts/openbao-vault
# Test template rendering
helm template test-release charts/openbao-vault \
-f charts/openbao-vault/values/openbao-vault-dedicated.yaml
# Dry run installation
helm install --dry-run test-release charts/openbao-vault \
-f charts/openbao-vault/values/openbao-vault-dedicated.yaml# Debug specific template
helm template test-release charts/openbao-vault \
-f charts/openbao-vault/values/openbao-vault.yaml --debug- Fork the repository
- Create a feature branch
- Add your changes
- Test with
helm lintandhelm template - Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Documentation: See
docs/directory for detailed guides - Issues: Report bugs via GitHub Issues
- Discussions: Use GitHub Discussions for questions
- OpenBao - Open source fork of HashiCorp Vault
- boyroywax/nfs-server - Custom NFS server for Kubernetes