Production Guides
Helm Chart Starter
Summary
This is a copy-paste reference Helm chart for a GoFr microservice. It assumes the application listens on the framework defaults — HTTP 8000, gRPC 9000, metrics 2121 — and uses /.well-known/alive and /.well-known/health for probes. Use it as the starting point for your own chart.
Prefer a maintained chart?
The reference chart below is intentionally minimal so you can read every line. If you'd rather depend on a maintained chart, the community chart at zop/service covers the same shape (Deployment + Service + optional Ingress/HPA + probes).
helm repo add zop https://helm.zop.dev
helm install my-app zop/service
Override values with -f values.yaml or --set; the chart's values.schema.json marks user-mutable fields with "mutable": true.
This is reference material, not a published chart. A future gofr-dev/gofr-k8s-starter repo could host a maintained version. For now, copy the files below into a chart/ directory in your service repo.
Layout
chart/
├── Chart.yaml
├── values.yaml
└── templates/
├── _helpers.tpl
├── deployment.yaml
└── service.yaml
Chart.yaml
apiVersion: v2
name: gofr-service
description: A reference Helm chart for a GoFr microservice
type: application
version: 0.1.0
appVersion: "0.1.0"
values.yaml
image:
repo: ghcr.io/example/my-gofr-service
tag: latest
pullPolicy: IfNotPresent
replicaCount: 2
service:
type: ClusterIP
httpPort: 8000
grpcPort: 9000
metricsPort: 2121
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env: {}
# DB_HOST: db.svc
# LOG_LEVEL: INFO
# TRACE_EXPORTER: otlp
# TRACER_URL: tempo:4317
envFromSecrets: []
# - my-db-credentials
ingress:
enabled: false
className: nginx
host: api.example.com
tls:
enabled: false
secretName: api-tls
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
podSecurityContext:
runAsNonRoot: true
runAsUser: 65532
fsGroup: 65532
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
The default ports (8000, 9000, 2121) match GoFr's defaults verified in pkg/gofr/default.go.
templates/_helpers.tpl
{{- define "gofr-service.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "gofr-service.fullname" -}}
{{- printf "%s-%s" .Release.Name (include "gofr-service.name" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "gofr-service.labels" -}}
app.kubernetes.io/name: {{ include "gofr-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version }}
{{- end -}}
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "gofr-service.fullname" . }}
labels: {{ include "gofr-service.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "gofr-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels: {{ include "gofr-service.labels" . | nindent 8 }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.service.metricsPort }}"
prometheus.io/path: "/metrics"
spec:
securityContext: {{ toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: app
image: "{{ .Values.image.repo }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.httpPort }}
- name: grpc
containerPort: {{ .Values.service.grpcPort }}
- name: metrics
containerPort: {{ .Values.service.metricsPort }}
env:
- name: HTTP_PORT
value: "{{ .Values.service.httpPort }}"
- name: GRPC_PORT
value: "{{ .Values.service.grpcPort }}"
- name: METRICS_PORT
value: "{{ .Values.service.metricsPort }}"
{{- range $k, $v := .Values.env }}
- name: {{ $k }}
value: {{ $v | quote }}
{{- end }}
{{- with .Values.envFromSecrets }}
envFrom:
{{- range . }}
- secretRef:
name: {{ . }}
{{- end }}
{{- end }}
livenessProbe:
httpGet:
path: /.well-known/alive
port: http
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /.well-known/health
port: http
initialDelaySeconds: 5
periodSeconds: 10
resources: {{ toYaml .Values.resources | nindent 12 }}
securityContext: {{ toYaml .Values.securityContext | nindent 12 }}
terminationGracePeriodSeconds: 30
A few choices worth calling out:
- The probe paths are GoFr's built-in endpoints.
/.well-known/aliveis cheap and exempt from auth by default;/.well-known/healthincludes dependency status and is more truthful for readiness. - Env vars
HTTP_PORT,GRPC_PORT,METRICS_PORTare set explicitly so the container ports and probe ports always agree with what GoFr actually binds. - The Prometheus scrape annotations point to
metricsPort. If your platform uses ServiceMonitor/PodMonitor instead, drop the annotations and add a separate template. terminationGracePeriodSeconds: 30gives GoFr's graceful shutdown time to drain in-flight requests.
templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "gofr-service.fullname" . }}
labels: {{ include "gofr-service.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- name: http
port: {{ .Values.service.httpPort }}
targetPort: http
- name: grpc
port: {{ .Values.service.grpcPort }}
targetPort: grpc
- name: metrics
port: {{ .Values.service.metricsPort }}
targetPort: metrics
selector:
app.kubernetes.io/name: {{ include "gofr-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
Optional: Ingress and HPA
Add templates/ingress.yaml gated on .Values.ingress.enabled and templates/hpa.yaml gated on .Values.autoscaling.enabled. Keep them off by default so the chart stays simple for first-time users.
Using the chart
helm upgrade --install my-api ./chart \
--set image.tag=$(git rev-parse --short HEAD) \
--set 'env.LOG_LEVEL=INFO' \
--wait --timeout 5m
Pin the image tag to a Git SHA in production, never latest.
Probes choice
If /.well-known/health is slow because it pings databases, you can split:
- Liveness →
/.well-known/alive(process is up) - Startup probe →
/.well-known/health(deps reachable; tolerate failure during boot) - Readiness →
/.well-known/aliveonce startup passes, to avoid removing pods on transient DB blips
Tune per service.