Helm Chart — Generic Spring Boot / Node.js Microservice
Production-grade Helm chart with HPA, PDB, Service, Ingress, NetworkPolicy, ServiceMonitor and External Secrets — drop in any container image and ship.
One opinionated chart that 90% of microservices can use. Wire it up by overriding image.repository, image.tag and env vars in your values-<env>.yaml. Includes the boring-but-critical defaults: resource requests, probes, PDB, anti-affinity, security context, and Prometheus scrape annotations.
1. Chart.yaml
Start with a clean chart skeleton, no junk templates.
apiVersion: v2
name: microservice
description: Cloudadhar generic microservice chart
type: application
version: 1.0.0
appVersion: "1.0.0"
maintainers:
- name: Cloudadhar
url: https://www.trainwithcloudadhar.com2. values.yaml — sane defaults
Override only what changes per service.
image:
repository: ghcr.io/cloudadhar/api
tag: "" # default to .Chart.AppVersion if empty
pullPolicy: IfNotPresent
replicaCount: 3
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
host: api.example.com
tls: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: 10m
resources:
requests: { cpu: 100m, memory: 256Mi }
limits: { cpu: 500m, memory: 512Mi }
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
probes:
liveness: { path: /healthz, initialDelaySeconds: 15, periodSeconds: 10 }
readiness: { path: /ready, initialDelaySeconds: 5, periodSeconds: 5 }
env: []
# - name: SPRING_PROFILES_ACTIVE
# value: prod
externalSecret:
enabled: false
store: aws-secrets-manager
refreshInterval: 1h
data:
- secretKey: DB_PASSWORD
remoteRef: prod/api/db-password
monitoring:
enabled: true # creates ServiceMonitor for Prometheus3. Deployment template
Anti-affinity spreads pods across nodes. Non-root securityContext makes Pod Security Standards happy.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "microservice.fullname" . }}
labels: {{- include "microservice.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels: {{- include "microservice.selectorLabels" . | nindent 6 }}
template:
metadata:
labels: {{- include "microservice.selectorLabels" . | nindent 8 }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.service.targetPort }}"
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
env: {{- toYaml .Values.env | nindent 12 }}
livenessProbe:
httpGet: { path: {{ .Values.probes.liveness.path }}, port: http }
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
readinessProbe:
httpGet: { path: {{ .Values.probes.readiness.path }}, port: http }
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
resources: {{- toYaml .Values.resources | nindent 12 }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels: {{- include "microservice.selectorLabels" . | nindent 20 }}
topologyKey: kubernetes.io/hostname4. HPA + PDB
HPA scales pods on CPU. PDB prevents the whole deployment from going down during node drains.
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "microservice.fullname" . }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "microservice.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "microservice.fullname" . }}
spec:
minAvailable: 1
selector:
matchLabels: {{- include "microservice.selectorLabels" . | nindent 6 }}
{{- end }}5. Install
Test render before applying, then `helm upgrade --install`.
# Lint + render
helm lint .
helm template api . -f values-prod.yaml
# Install / upgrade
helm upgrade --install api . \
-n app --create-namespace \
-f values-prod.yaml \
--set image.tag=$GIT_SHA \
--wait --timeout 5m
# Roll back if something looks off
helm rollback apiWant me to implement this in your environment?
Cloudadhar offers paid setup engagements: I bring the template above into your AWS account, wire it up to your CI/CD, and walk your team through it.