DocsFeaturesCloud Secrets

Cloud Secrets

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.

Supported providers:

  • AWS Secrets Manager
  • GCP Secret Manager

Configuration

Secret providers are configured in the Lifecycle global_config database table under the secretProviders key.

Default Configuration

Both AWS and GCP providers are enabled by default. The ClusterSecretStore created by your platform team determines which provider actually works in your cluster.

  • On AWS (EKS): Use {{aws:path:key}} syntax. The aws-secretsmanager ClusterSecretStore handles the request.
  • On GCP (GKE): Use {{gcp:path:key}} syntax. The gcp-secretmanager ClusterSecretStore handles the request.

If you reference a provider that doesn’t have a ClusterSecretStore in your cluster, the secret will fail to sync and a warning will be logged.

Configuration Fields

FieldRequiredDescription
enabledYesEnable this provider
clusterSecretStoreYesName of the ClusterSecretStore CR configured by your platform team
refreshIntervalYesHow often ESO syncs secrets (e.g., 1h, 30m)
allowedPrefixesNoRestrict which secret paths can be referenced (empty = allow all)

If you’re unsure which clusterSecretStore to use, check with your platform team. They configure the External Secrets Operator and ClusterSecretStore as part of cluster setup.

Syntax

Reference secrets using the following pattern:

ComponentDescriptionExample
providerCloud provider (aws or gcp)aws
secret-pathFull path to the secretmyapp/database-credentials
keyJSON key within the secret (optional)password

If your secret is a plaintext value (not JSON), omit the key portion: {"{{aws:myapp/api-key}}"}.

Basic Usage

JSON Secrets

For secrets stored as JSON objects, specify the key to extract:

If your AWS secret myapp/rds-credentials contains:

Your pod will receive:

  • DB_USERNAME=admin
  • DB_PASSWORD=supersecret
  • DB_HOST=db.example.com

Plaintext Secrets

For secrets stored as plain strings, omit the key:

Nested JSON

For nested JSON structures, use dot notation:

For a secret containing:

Where Secrets Work

The secret syntax works in all env blocks:

GitHub Services

Docker Services

Webhooks

⚠️

Helm and Codefresh deployment types do not currently support cloud secrets.

Mixing Secrets with Template Variables

You can combine cloud secrets with regular template variables in the same env block:

Multiple Providers

Both providers are enabled by default. If your platform team has configured ClusterSecretStores for both AWS and GCP, you can reference secrets from either in the same deployment:

Each provider requires its own ClusterSecretStore. Contact your platform team if you need multi-cloud secret access.

Error Handling

Lifecycle uses a “warn and continue” approach for secret errors:

ScenarioBehavior
Secret not foundWarning logged, env var not set, deployment continues
Key not found in JSONWarning logged, env var not set
Provider not enabledWarning logged, env var not set
Invalid syntaxValidation error at deploy time
⚠️

If a required secret is missing, your application may fail to start or behave unexpectedly. Always verify your secrets exist before deploying.

Best Practices

Use Descriptive Secret Paths

Organize secrets with meaningful paths:

Store related credentials in a single JSON secret:

Then reference individual keys:

Avoid Secrets in Build Logs

Be cautious when using secrets in build-time environment variables, as they may appear in build logs. Prefer injecting secrets at runtime when possible.

Troubleshooting

Secret Not Injected

  1. Check the secret exists in AWS Secrets Manager / GCP Secret Manager
  2. Verify the path matches exactly (case-sensitive)
  3. Check the key name if using JSON secrets
  4. Review Lifecycle logs for warnings about missing secrets
  5. Verify provider is enabled in global configuration

Syntax Errors

Common syntax mistakes:

Permission Denied

If you see permission errors:

  1. Verify the External Secrets Operator has access to the secret path
  2. Check IAM policies allow access to the specific secret
  3. Contact your platform team if the secret is in a restricted path

Wrong Provider for Cluster

If you use {{gcp:...}} on an AWS cluster (or vice versa), the ExternalSecret will fail to sync because the ClusterSecretStore doesn’t exist. Check Lifecycle logs for warnings like “ClusterSecretStore not found”.

Prerequisites

For cloud secrets to work, your platform team must set up:

ComponentDescription
External Secrets OperatorKubernetes operator that syncs secrets from cloud providers
ClusterSecretStoreCR that configures ESO to connect to AWS Secrets Manager or GCP Secret Manager
IAM/Workload IdentityPermissions for ESO to read secrets (IRSA for AWS, Workload Identity for GCP)

The clusterSecretStore value in your Lifecycle config must match the ClusterSecretStore name configured by your platform team.

Build-Time Secrets

The cloud secrets described above are runtime secrets — they get mounted into your running pods. Lifecycle also supports build-time secrets, which are injected during the Docker image build as build arguments.

You use the same {{provider:path:key}} syntax. The difference is where the secret is consumed: build-time secrets are available during docker build, while runtime secrets are mounted into the final running container.

Common use cases:

  • Private NPM registry tokens (e.g., NPM_TOKEN for .npmrc)
  • Private package repository credentials
  • License keys required during compilation
  • Authentication tokens for private base images

Configuration

Define build-time secrets in the env block of your service’s docker configuration, just like runtime secrets:

Your Dockerfile then references them as build arguments:

How It Works

When you reference a {{provider:path:key}} value in a build service’s env block, Lifecycle resolves the secret from your cloud provider before the build starts. The resolved values are passed to Docker as build arguments (--build-arg), making them available as ARG variables in your Dockerfile.

Lifecycle waits for the secret to sync before starting the build, so you don’t need to worry about race conditions with the External Secrets Operator.

Both Buildkit and Kaniko build engines support build-time secrets. The secret resolution process is the same regardless of which engine you use.

Security Considerations

⚠️

Build-time secrets are not masked in build logs. Any RUN command that prints environment variables (e.g., echo $NPM_TOKEN, env, printenv) will expose secret values in the build output. Build logs are fetched on-demand from Kubernetes and are visible to anyone with access to the Lifecycle UI.

To minimize exposure:

  • Never print environment variables during build steps
  • Delete credential files after use (e.g., RUN rm -f .npmrc after npm install)
  • Use multi-stage builds so secrets are only present in the builder stage, not the final image
  • Prefer runtime secrets when possible — only use build-time secrets when the value is genuinely needed during image construction

Troubleshooting

Secret Not Available During Build

  1. Verify the syntax — make sure your env var uses the {{provider:path:key}} pattern
  2. Check that the secret exists in AWS Secrets Manager or GCP Secret Manager
  3. Confirm the ExternalSecret synced — Lifecycle waits for sync before building, but if the secret path is wrong or permissions are missing, the sync will fail
  4. Check build logs for warnings about missing or unresolved secrets

Secret Value Is Empty

If your secret is stored as a plaintext string (not JSON), you can omit the key. But if the secret is JSON, you must specify which key to extract.

Dockerfile Not Receiving the Value

Make sure your Dockerfile declares the build argument with ARG: