Thank you for installing **{{ .Chart.Name }}**.

Release: {{ .Release.Name }} / namespace {{ .Release.Namespace }}

Services use ClusterIP; expose via ingress, gateway, or kubectl port-forward. **reverseproxy:** with **`components.reverseproxy.kubernetesUpstreamHosts: true`** (default), YARP destinations are set by env to **`http://<fullname>-server:<serverPort>/`** and **`http://<fullname>-client:<clientPort>/`** (same pattern for single-node and HA replica counts). Docker Compose uses **`docker-compose.override.yml`** env to **`http://server:…`** / **`http://client:…`** instead (no Helm).

------------------------------------------------------------
## Components

- server: {{ include "certs-ui.fullname" . }}-server:{{ .Values.components.server.service.port }}
- client: {{ include "certs-ui.fullname" . }}-client:{{ .Values.components.client.service.port }}
- reverseproxy: {{ include "certs-ui.fullname" . }}-reverseproxy:{{ .Values.components.reverseproxy.service.port }}

Port-forward API example:

  kubectl port-forward svc/{{ include "certs-ui.fullname" . }}-server {{ .Values.components.server.service.port }}:{{ .Values.components.server.service.port }} -n {{ .Release.Namespace }}

------------------------------------------------------------
## Images

Image tag: **`global.image.tag` when set**, else **`components.*.image.tag`**. There is **no** fallback to Chart `appVersion`; set tags explicitly (chart default is `latest` per component when **`global.image.tag`** is omitted). To force one tag for all images, uncomment **`global.image.tag`** in **`values.yaml`** (see commented examples).

**imagePullPolicy**: **`global.image.pullPolicy` when set**, else **`components.*.image.pullPolicy`**, else **`IfNotPresent`** in the template. Chart **`values.yaml`** sets per-component **`pullPolicy: IfNotPresent`** and omits **`global.image.pullPolicy`** so components win by default. Uncomment the example under **`global.image`** to override all. Values may use **`always`** / **`Always`** (normalized for the Pod spec).

With **`Always`**, the chart sets pod annotation **`rollme`**: by default **`r<Release.Revision>-<unixEpoch>`** so each **`helm upgrade`** bumps the revision even when the tag is unchanged. For **`helm template`** output committed to git, revision is usually **1** and epoch is frozen until you re-render — then pass **`global.rolloutNonce`** from CI (unique per deploy, e.g. pipeline id) so the applied manifest changes every image push. Pin **`global.rollme`** to a string you bump when you need a stable, deterministic rollout key.

With **`IfNotPresent`** (default), **`rollme`** is omitted; the node may keep a cached layer for a mutable tag even if you replace the tag in the registry.

Pod annotation **certs-ui.io/image** is the resolved `registry/repository:tag` for debugging.

Optional per workload under **`components.<name>`**: **`replicaCount`** (default **1**; stateless **client** / **reverseproxy** are safe to raise on an HA cluster), **`livenessProbe`**, **`startupProbe`**, **`readinessProbe`**, **`resources`** (standard Kubernetes container fields). Omit a key to leave it unset.

When **`replicaCount` > 1**, the chart creates a **PodDisruptionBudget** (`minAvailable: 1`) for that component.

**Postgres leases (short-lived):** **`certs-ui-bootstrap`** — one pod runs coordination DDL + default admin, then releases the lease. **`certs-ui-renewal-sweep`** — one pod runs each renewal sweep, then releases. All **server** pods are **symmetric** (no elected primary in DI). Interactive ACME and **HTTP-01** use **PostgreSQL**. The **server** `Service` defaults to **no session affinity**. Set **`components.server.service.sessionAffinity.enabled: true`** only if you want **`ClientIP`** stickiness. Stale lease rows expire by TTL if a pod dies mid-section.

**Persistence:** the chart does **not** mount application data PVCs by default (ACME and identity state are in **PostgreSQL**). Add entries under **`components.server.persistence.volumes`** only if you need extra local scratch or sidecar files.

------------------------------------------------------------
## Config

Root keys `certsServerConfig`, `certsServerSecrets`, `certsClientRuntime` feed templated `configMapFile` / `secretsFile` content when `tpl: true`.

Use `existingConfigMap` / `existingSecret` to mount resources created outside the chart. With `keep: true`, existing objects are not replaced on upgrade if already present.

------------------------------------------------------------
## Data and database

PostgreSQL: **external** — set **`certsServerSecrets.certsEngineConfiguration.connectionString`** (or mount **`existingSecret`** with the same **`appsecrets.json`** shape). The app waits via in-process **`PostgresStartupWait`** during migrations and bootstrap. Local dev: **`docker-compose`** **`postgres`** service (not part of this chart).

Health while starting: **`GET /health/startup`** (JSON phase snapshot); traffic should use **`GET /health/ready`** (503 until bootstrap completes). Server **`startupProbe`** defaults to **`/health/ready`** (see chart **`values.yaml`**).

------------------------------------------------------------
## Uninstall

  helm uninstall {{ .Release.Name }} -n {{ .Release.Namespace }}
