planpalto-helm — render pipeline

How helm install turns the chart on disk into running objects on the fakhri-k8 EKS cluster.

live / rendered value
static linework
Npipeline stage

Render pipeline — 6 stages

1Inputs

Chart.yaml · values.yaml · templates/* · _helpers.tpl · optional -f envs/a.yaml

paths
2Command

You run one helm command. Helm reads the chart and any override file.

flags+values
3Render

Go templating merges values + .Release.*, walks templates/, emits one multi-doc YAML.

YAML
4Submit

Manifest sent to the k8s API server. Helm records the revision in a Secret.

objects
5Reconcile

Controllers act: Deployment → ReplicaSet → Pod. Scheduler places Pods on EKS nodes.

pods
6Add-ons

ESO, AWS Load Balancer Controller, and metrics-server finish the job.

1 · Inputs on disk

planpalto-helm/ ├─ Chart.yaml ├─ values.yaml apps: map (5 entries) + deps + global ├─ templates/ │ ├─ namespace.yaml │ ├─ apps.yaml {{ range .Values.apps }} │ ├─ deps.yaml redis, nats │ ├─ external-secrets.yaml │ ├─ ingress.yaml │ └─ seed-job.yaml └─ _helpers.tpl planpal.namespace / .storeName / .image + override: -f envs/a.yaml

2 · command ↓

helm upgrade --install planpal ./planpalto-helm -f envs/a.yaml

upgrade --install = create if absent, else new revision.

3 · Render — Go template engine (client-side, in Helm)

Merged values

values.yaml
  + -f envs/a.yaml   (override)
  + .Release.Name planpal
  + .Release.Namespace

Go template engine

  • {{ .Values.x }} substitute
  • {{ if }} / {{ with }} conditional / scope
  • {{ range }} loop over apps map
  • {{ include "planpal.namespace" . }} named template from _helpers.tpl
  • | default | toJson | toString pipes

Multi-doc YAML

---
kind: Namespace
...
---
kind: Deployment
metadata:
  name: planpal-backend
...
---   (× many docs)

5 · Reconcile

Deployment ReplicaSet Pod scheduler EKS node

Controllers reconcile the declared state. HPA scales replicas via metrics-server.

6 · Cluster add-ons

ExternalSecret▶ ESOreads AWS Secrets Manager → real k8s Secret
Ingress▶ ALB ctrlprovisions an ALB
HPA▶ metrics-serverfeeds CPU/mem → scale

Template → object mapbill of materials · rendered from the chart, exact counts

Source fileRendered objects
templates/namespace.yaml1 Namespace · 1 ServiceAccount planpal-backend
templates/apps.yaml5 Deployment · 2 Service · 5 HPA · 2 PDB  ← one {{ range }} loop, see DETAIL A
templates/deps.yaml2 Deployment redis, nats · 2 Service  (these 2 also count toward the 7 Deployment total)
templates/external-secrets.yaml1 ClusterSecretStore · 4 ExternalSecret
templates/ingress.yaml1 Ingress (ALB)
templates/seed-job.yaml1 Job seed-admin (runs once)
Σ total7 Deployment · 4 Service · 5 HPA · 2 PDB · 1 Ingress · 1 Job · 1 Namespace · 1 ServiceAccount · 1 ClusterSecretStore · 4 ExternalSecret

Detail A

One {{ range .Values.apps }} over a 5-entry map builds 5 Deployments. Conditionals add the extras: a Service if service:, an HPA if hpa:, a PDB if pdb:.

backendservice:hpa:pdb:
frontendservice:hpa:pdb:
schedule-workerhpa:
notification-workerhpa:
ai-workerhpa:
{{ range .Values.apps }}  ×5
Deploymentif service:Serviceif hpa:HPAif pdb:PDB
Deploymentif service:Serviceif hpa:HPAif pdb:PDB
Deploymentif hpa:HPA
Deploymentif hpa:HPA
Deploymentif hpa:HPA

apps.yaml → 5 Deployment · 2 Service · 5 HPA · 2 PDB. The redis + nats Deployments come from deps.yaml, not this loop — they are the other 2 in the total of 7.

Detail B

Each helm install lands in its own namespace + ALB + ClusterSecretStore, all defaulting to .Release.Name via _helpers.tpl.

planpal-a

Namespace
planpal-a
Ingress → ALB
(own LB)
ClusterSecretStore
planpal-a-aws-planpal

planpal-b

Namespace
planpal-b
Ingress → ALB
(own LB)
ClusterSecretStore
planpal-b-aws-planpal

planpal.namespace = default .Release.Name → planpal-a / planpal-b planpal.storeName = per-release CSS The empty gutter is the isolation: no connector crosses it. Two releases share nothing on the cluster.

Chart
planpalto-helm
Release
planpal
Cluster
fakhri-k8 (EKS)
Release state
Secret sh.helm.release.v1
Scale
NTS
Sheet
1 of 1
Date
2026-09-01