A Helm library chart for shipping stateless services, scheduled jobs, observability, and ingress TLS without repeating Kubernetes boilerplate.
helm-serve is a DRY Helm library chart that standardizes how teams render Deployment, CronJob, Service, Ingress, ServiceMonitor, and PrometheusRule resources. You describe the workload once in values.yaml; the chart expands that into production-ready manifests with sane defaults, tpl support, and a clean extension model.
- One library, two workload modes: run long-lived web/API services and scheduled jobs with the same abstraction.
- Low YAML surface area: keep application charts small while still producing full Kubernetes resources.
- Built for day-2 operations: probes, resources, metadata injection, metrics wiring, and ingress are already modeled.
- Consumer-owned naming: keep your own helper conventions through
templatePrefixinstead of adopting someone else's naming scheme.
v0.2.2 introduced the observability layer. v0.2.3-beta1 builds on top of that and closes a real production gap: Ingress TLS is now a first-class capability instead of a hand-written add-on.
| Area | v0.2.2 | v0.2.3-beta1 |
|---|---|---|
| Observability | Metrics Service, ServiceMonitor, PrometheusRule |
Preserved as-is |
| Ingress TLS | Not modeled by the library | Built in with 3 TLS modes |
| Multi-domain HTTPS | Manual/custom templating needed | Supported via ingress.tls.hosts |
| SNI / multiple certs | Manual/custom templating needed | Supported via ingress.tls.secrets[] |
tpl in TLS fields |
N/A | Supported for hosts and secretName |
- Auto mode
Enable TLS and provide one secret. The chart uses
ingress.rule.hostautomatically. - Multi-host, single-secret mode Perfect for SAN certificates covering multiple domains.
- Power-user multi-secret mode
Provide
ingress.tls.secrets[]for true SNI setups where different hosts terminate with different secrets.
This is the practical difference between 0.2.2 and the current prerelease: you no longer need to fork the ingress template just to express real HTTPS topologies.
- Deployment and CronJob modes from the same library chart.
- Ingress, Service, and metrics resources rendered only when enabled.
- Ingress TLS support for single-host, multi-host, and multi-secret/SNI setups.
- Prometheus-ready observability with metrics
Service, optionalServiceMonitor, and optionalPrometheusRule. - Probe support everywhere with
startupProbe,livenessProbe, andreadinessProbeon both Deployments and CronJobs. tpl-aware values across env vars, metrics configuration, relabelings, ingress hosts, and TLS secret names.- Production-oriented defaults like resource requests/limits,
revisionHistoryLimit: 0, graceful termination, and metadata injection. - Consumer-side naming control through
templatePrefix.
helm-serve is a library chart. You consume it from an application chart; you do not install it directly as a standalone release.
Add it to your application's Chart.yaml:
dependencies:
- name: helm-serve
repository: oci://ghcr.io/btungut
version: 0.2.3-beta1Then update dependencies:
helm dependency updateIf you want a clean rebuild of vendored dependencies:
rm -rf charts/ && helm dependency update .deployment:
replicaCount: 2
image:
repository: myrepo/my-app
tag: v1.0.0
containerPort: 8080
service:
enabled: true
port: 80
ingress:
enabled: true
className: nginx
rule:
host: api.mydomain.com
path: /This renders a Deployment, a Service, and an Ingress with the service backend already wired.
cronJob:
schedule: "0 2 * * *"
image:
repository: myrepo/my-batch-job
tag: v1.0.0
env:
JOB_TYPE: data-processingThis renders a CronJob with the same resource, probe, env, config, and secret primitives available in Deployment mode.
deployment:
containerPort: 8080
image:
repository: myrepo/my-app
tag: v1.0.0
service:
enabled: true
port: 80
metrics:
enabled: true
metricsPort: 9090
path: /metrics
serviceMonitor:
enabled: true
prometheusRule:
rules:
- alert: HighErrorRate
expr: 'sum(rate(http_requests_total{status=~"5.."}[5m])) > 0.05'
for: 10mWhen enabled, the chart adds a dedicated metrics Service and can optionally emit ServiceMonitor and PrometheusRule resources for Prometheus Operator based stacks.
ingress:
enabled: true
className: nginx
rule:
host: api.mydomain.com
path: /
tls:
enabled: true
hosts:
- api.mydomain.com
- www.mydomain.com
secretName: my-app-tlsFor SNI-style setups, switch from hosts to secrets[] and assign different certificates per host set.
helm-serve intentionally keeps naming responsibility in the consumer chart.
If your chart exposes helpers such as my-app.fullname, pass the prefix below and the library will call your templates instead of hardcoding names:
templatePrefix: my-appThat allows the library to stay reusable without forcing a naming opinion across teams.
The test/ directory is effectively a cookbook. Read the files in order if you want to understand the surface area quickly.
| File | Focus | What it demonstrates |
|---|---|---|
values-basic.yaml |
Minimal service | Deployment + Service + Ingress |
values-middle.yaml |
Day-2 baseline | Shared values, ConfigMaps, Secrets, NodePort, env vars, metrics Service |
values-full.yaml |
Full surface | Labels, annotations, probes, templatePrefix, tpl, ingress TLS, full metrics stack |
values-metrics.yaml |
Observability | Dedicated walkthrough of the metrics: block |
values-ingress-tls.yaml |
HTTPS patterns | All supported ingress TLS modes, including cert-manager-friendly examples |
values-cronjob.yaml |
Scheduled workloads | CronJob mode with execution policy and probe examples |
The library selects its primary workload resource from the top-level values you provide:
deploymentpresent: renders aDeploymentcronJobpresent: renders aCronJob
Secondary resources such as Service, Ingress, and observability objects are rendered independently based on their own enabled flags.
| Parameter | Description | Default |
|---|---|---|
templatePrefix |
Prefix used to invoke naming templates from the consumer chart. | {{ .Chart.Name }} |
shared |
Shared variables that can be consumed from tpl expressions elsewhere. |
{} |
| Parameter | Description | Default |
|---|---|---|
deployment.replicaCount |
Number of desired pods. | 1 |
deployment.revisionHistoryLimit |
Old ReplicaSets to retain. | 0 |
deployment.terminationGracePeriodSeconds |
Graceful termination period. | 10 |
deployment.image.repository |
Container image repository. | "" |
deployment.image.tag |
Container image tag. | "" |
deployment.image.pullPolicy |
Image pull policy. | IfNotPresent |
deployment.imagePullSecrets |
Image pull secrets. | [] |
deployment.containerPort |
Main application container port. | Required |
deployment.env |
Environment variables, with tpl support. |
{} |
deployment.configMaps |
ConfigMaps to inject. | [] |
deployment.secrets |
Secrets to inject. | [] |
deployment.resources |
CPU and memory requests/limits. | cpu: 200m / memory: 256Mi |
deployment.startupProbe |
Startup probe definition. | {} |
deployment.livenessProbe |
Liveness probe definition. | {} |
deployment.readinessProbe |
Readiness probe definition. | {} |
deployment.labels |
Extra Deployment labels. | {} |
deployment.podAnnotations |
Extra Pod annotations. | {} |
| Parameter | Description | Default |
|---|---|---|
service.enabled |
Enable Service creation. | false |
service.name |
Override the default service name. | "" |
service.type |
ClusterIP, NodePort, or LoadBalancer. |
ClusterIP |
service.port |
Service port. | Required if enabled |
service.nodePort |
Fixed NodePort value. | nil |
service.labels |
Extra Service labels. | {} |
| Parameter | Description | Default |
|---|---|---|
ingress.enabled |
Enable Ingress creation. | false |
ingress.className |
Ingress class name. | Required if enabled |
ingress.annotations |
Ingress annotations. | {} |
ingress.rule.host |
Primary host. | Required if enabled |
ingress.rule.path |
Request path. | Required if enabled |
ingress.rule.pathType |
Prefix, Exact, or ImplementationSpecific. |
Prefix |
ingress.tls.enabled |
Enable TLS block rendering. | false |
ingress.tls.secretName |
Secret name for auto or multi-host single-secret mode. Supports tpl. |
Required when TLS is enabled and secrets[] is not used |
ingress.tls.hosts |
Explicit host list for single-secret mode. Supports tpl. |
Defaults to [ingress.rule.host] |
ingress.tls.secrets |
List of { hosts, secretName } pairs for multi-secret/SNI mode. Supports tpl. |
[] |
| Parameter | Description | Default |
|---|---|---|
metrics.enabled |
Master switch for the metrics stack. | false |
metrics.metricsPort |
Container port exposing /metrics. Supports tpl. |
Required if enabled |
metrics.path |
Path served by the metrics endpoint. | nil |
metrics.labels |
Extra labels for metrics resources. | {} |
metrics.serviceMonitor.enabled |
Render a ServiceMonitor. |
false |
metrics.serviceMonitor.interval |
Scrape interval. | 30s |
metrics.serviceMonitor.scrapeTimeout |
Scrape timeout. | 10s |
metrics.serviceMonitor.scheme |
http or https. |
http |
metrics.serviceMonitor.honorLabels |
Honor target-provided labels. | nil |
metrics.serviceMonitor.relabelings |
Prometheus relabel rules, with tpl support. |
nil |
metrics.serviceMonitor.metricRelabelings |
Metric relabel rules, with tpl support. |
nil |
metrics.prometheusRule.rules |
Alerting or recording rules. | [] |
| Parameter | Description | Default |
|---|---|---|
cronJob.schedule |
Cron expression. | Required |
cronJob.concurrencyPolicy |
Allow, Forbid, or Replace. |
nil |
cronJob.successfulJobsHistoryLimit |
Successful job history retention. | nil |
cronJob.failedJobsHistoryLimit |
Failed job history retention. | nil |
cronJob.suspend |
Suspend execution. | nil |
cronJob.startingDeadlineSeconds |
Deadline for missed schedules. | nil |
cronJob.backoffLimit |
Retry count before failure. | nil |
cronJob.activeDeadlineSeconds |
Job timeout. | nil |
cronJob.ttlSecondsAfterFinished |
Post-completion cleanup TTL. | nil |
cronJob.restartPolicy |
OnFailure or Never. |
OnFailure |
cronJob.image.repository |
Container image repository. | "" |
cronJob.image.tag |
Container image tag. | "" |
cronJob.image.pullPolicy |
Image pull policy. | IfNotPresent |
cronJob.imagePullSecrets |
Image pull secrets. | [] |
cronJob.env |
Environment variables, with tpl support. |
{} |
cronJob.configMaps |
ConfigMaps to inject. | [] |
cronJob.secrets |
Secrets to inject. | [] |
cronJob.resources |
CPU and memory requests/limits. | cpu: 200m / memory: 256Mi |
cronJob.startupProbe |
Startup probe definition. | {} |
cronJob.livenessProbe |
Liveness probe definition. | {} |
cronJob.readinessProbe |
Readiness probe definition. | {} |
cronJob.labels |
Extra CronJob labels. | {} |
cronJob.podAnnotations |
Extra Pod annotations. | {} |
shared:
primaryHost: api.mydomain.com
metricsPort: "9090"
deployment:
containerPort: 8080
env:
APP_INSTANCE: "{{ .Release.Name }}"
ingress:
rule:
host: "{{ .Values.shared.primaryHost }}"
tls:
enabled: true
secretName: "{{ .Release.Name }}-tls"
metrics:
enabled: true
metricsPort: "{{ .Values.shared.metricsPort }}"This keeps repeated values centralized while still letting the library render concrete manifests.
Every workload receives metadata environment variables that are useful for logs, traces, and self-identification:
| Variable | Source |
|---|---|
HELM_Version |
.Chart.Version |
HELM_AppVersion |
.Chart.AppVersion |
HELM_Description |
.Chart.Description |
HELM_Namespace |
.Release.Namespace |
K8S_Namespace |
Downward API (metadata.namespace) |
helm-serve exposes startupProbe, livenessProbe, and readinessProbe for CronJob pods as well as Deployments. That is useful for long-running jobs, sidecar-style workers, and scheduled tasks that still need health semantics.
If you already use helm-serve v0.2.2, the upgrade is backward compatible. However, you can now remove custom ingress template overrides and use the built-in TLS support:
# Custom ingress template needed for HTTPS
ingress:
enabled: true
className: nginx
rule:
host: api.mydomain.com
# ...then you'd fork templates to add TLS block manually# Built-in TLS support — no template fork needed
ingress:
enabled: true
className: nginx
rule:
host: api.mydomain.com
tls:
enabled: true
secretName: my-tls-secretUse cases now natively supported:
- Single domain + TLS: Set
ingress.tls.secretName; the chart auto-usesingress.rule.host. - Multiple domains + one cert (SAN): Use
ingress.tls.hosts: [api.example.com, www.example.com]withsecretName. - SNI / per-domain certs: Use
ingress.tls.secrets[]for advanced setups.
No migration code needed — old values still work, and you can gradually adopt the new TLS parameters.
This project is licensed under the Apache License 2.0.