diff --git a/src/pages/docs/features/secrets.mdx b/src/pages/docs/features/secrets.mdx
index de87eb73..b98378b4 100644
--- a/src/pages/docs/features/secrets.mdx
+++ b/src/pages/docs/features/secrets.mdx
@@ -12,7 +12,7 @@ import { Callout } from "nextra/components";
## Overview
-Lifecycle integrates with cloud secret providers to securely inject secrets into your ephemeral environments. You can reference secrets directly in your `lifecycle.yaml` environment variables using a template syntax, and Lifecycle handles the rest.
+Lifecycle integrates with cloud secret providers to securely inject secrets into your ephemeral environments. You can reference secrets directly in your `lifecycle.yaml` environment variables and native Helm custom values using a template syntax, and Lifecycle handles the rest.
Supported providers:
@@ -153,7 +153,7 @@ For a secret containing:
## Where Secrets Work
-The secret syntax works in all `env` blocks:
+The secret syntax works in all `env` blocks and in native Helm custom values:
### GitHub Services
@@ -197,7 +197,102 @@ environment:
```
- Helm and Codefresh deployment types do not currently support cloud secrets.
+ Codefresh Helm deployments do not support cloud secrets in Helm custom values.
+ Use native Helm deployment for secret-backed Helm values.
+
+
+### Native Helm Custom Values
+
+Native Helm deployments support full-value secret references in `helm.chart.values`.
+Lifecycle mounts the synced Kubernetes Secret into the Helm job and passes the
+value with Helm `--set-file`, so the resolved secret value is read from a file
+instead of being written into the Helm command.
+
+```yaml
+services:
+ - name: postgres
+ helm:
+ repository: myorg/orders-api
+ branchName: main
+ deploymentMethod: native
+ chart:
+ name: postgresql
+ repoUrl: https://charts.example.com
+ values:
+ - "auth.username={{aws:myapp/postgres:username}}"
+ - "auth.password={{aws:myapp/postgres:password}}"
+ - "auth.database=orders"
+```
+
+Lifecycle renders the plain value normally and turns the secret-backed values
+into `--set-file` arguments:
+
+```bash
+helm upgrade --install postgres bitnami/postgresql \
+ --set auth.database=orders \
+ --set-file auth.username=/var/run/lifecycle/helm-secrets/.../helm.auth.username... \
+ --set-file auth.password=/var/run/lifecycle/helm-secrets/.../helm.auth.password...
+```
+
+Only complete Helm values can be secret references:
+
+```yaml
+chart:
+ values:
+ # Correct
+ - "auth.password={{aws:myapp/postgres:password}}"
+
+ # Wrong: partial interpolation is not supported for Helm secret values
+ - "databaseUrl=postgres://app:{{aws:myapp/postgres:password}}@postgres:5432/orders"
+```
+
+For composite values, put the full value in your cloud secret and reference it
+directly:
+
+```yaml
+chart:
+ values:
+ - "databaseUrl={{aws:myapp/postgres:databaseUrl}}"
+```
+
+Native Helm custom values can still use regular Lifecycle template variables:
+
+```yaml
+services:
+ - name: api
+ requires:
+ - name: postgres
+ helm:
+ deploymentMethod: native
+ chart:
+ name: api
+ values:
+ - "env.DATABASE_HOST={{postgres_internalHostname}}"
+ - "env.DATABASE_PASSWORD={{aws:myapp/postgres:password}}"
+```
+
+If the same Helm key is set more than once, Lifecycle resolves the final winning
+value first, then decides whether to pass it with `--set` or `--set-file`:
+
+```yaml
+chart:
+ values:
+ # Secret value wins because it is later
+ - "env.API_TOKEN={{api_internalHostname}}"
+ - "env.API_TOKEN={{aws:myapp/api:token}}"
+
+ # Template value wins because it is later
+ - "env.CALLBACK_HOST={{aws:myapp/api:callbackHost}}"
+ - "env.CALLBACK_HOST={{api_internalHostname}}"
+```
+
+In the first pair, Lifecycle emits `--set-file env.API_TOKEN=...`. In the
+second pair, Lifecycle emits `--set env.CALLBACK_HOST=...`.
+
+
+ Do not add Helm `--debug` or `--dry-run` args when using secret-backed Helm
+ values. Lifecycle rejects those combinations because Helm can print rendered
+ values into job logs.
## Mixing Secrets with Template Variables
@@ -238,12 +333,14 @@ env:
Lifecycle uses a "warn and continue" approach for secret errors:
-| Scenario | Behavior |
-| --------------------- | ----------------------------------------------------- |
-| Secret not found | Warning logged, env var not set, deployment continues |
-| Key not found in JSON | Warning logged, env var not set |
-| Provider not enabled | Warning logged, env var not set |
-| Invalid syntax | Validation error at deploy time |
+| Scenario | Behavior |
+| ---------------------------------- | ----------------------------------------------------- |
+| Secret not found | Warning logged, env var not set, deployment continues |
+| Key not found in JSON | Warning logged, env var not set |
+| Provider not enabled | Warning logged, env var not set |
+| Invalid syntax | Validation error at deploy time |
+| Partial Helm secret interpolation | Validation error at deploy time |
+| Helm value secret ref on Codefresh | Unsupported deploy path error |
If a required secret is missing, your application may fail to start or behave
@@ -322,6 +419,19 @@ DB_PASSWORD: "{{path:key}}"
DB_PASSWORD: "{{aws:path:key}}"
```
+For native Helm custom values, the whole value after `=` must be the secret
+reference:
+
+```yaml
+chart:
+ values:
+ # Wrong - partial Helm secret interpolation
+ - "auth.password=prefix-{{aws:path:key}}"
+
+ # Correct
+ - "auth.password={{aws:path:key}}"
+```
+
### Permission Denied
If you see permission errors: