# How to Use Variable Substitution in Rudder CLI


Keep destination and warehouse credentials out of the YAML you commit. Write `{{ .VAR }}` references in specs, store the values in a var file, and pass `--var-file` when you apply.

You can also substitute non-secret values (for example an `account_id` that differs across development and production).

{{< warning >}}
**Variable substitution is experimental**.

Enable the `enableVarSubstitution` flag in your CLI configuration (or set `RUDDERSTACK_X_ENABLE_VAR_SUBSTITUTION=true`) before you apply specs that contain `{{ .VAR }}` references. Without it, Rudder CLI doesn't resolve the placeholders.
{{< /warning >}}

## Prerequisites

- [Install Rudder CLI]({{< ref "dev-tools/rudder-cli/installation.md" >}}) and [authenticate]({{< ref "dev-tools/rudder-cli/#authenticate-the-cli" >}}) against your workspace.
- Define or import YAML from the [Destination YAML Reference]({{< ref "dev-tools/rudder-cli/yaml-destinations.md" >}}) or [Account Resources YAML Reference]({{< ref "dev-tools/rudder-cli/yaml-accounts.md" >}}) that uses secret fields.

## Enable variable substitution

Set the flag in your environment (or the equivalent key in your CLI configuration):

```shell
export RUDDERSTACK_X_ENABLE_VAR_SUBSTITUTION=true
```

## Write variable references in YAML

Write each substituted field as `{{ .VARIABLE_NAME }}`. The name must match a key in the var file.

Use a variable for every **secret** field. Don't put the credential in the spec as a literal.

| Resource | Example secret keys | YAML reference |
| :--- | :--- | :--- |
| Amazon S3 destination (`role_based_auth: false`) | `access_key_id`, `access_key` | `{{ .AWS_ACCESS_KEY_ID }}`, `{{ .AWS_SECRET_ACCESS_KEY }}` |
| BigQuery account | `credentials` | `{{ .BQ_CREDENTIALS }}` |
| PostgreSQL account | `password` | `{{ .PG_PASSWORD }}` |
| Snowflake account | `privateKey`, `privateKeyPassphrase`, or `password` | `{{ .SF_PRIVATE_KEY }}`, `{{ .SF_PRIVATE_KEY_PASSPHRASE }}`, or `{{ .SF_PASSWORD }}` |

See [Amazon S3 configuration]({{< ref "dev-tools/rudder-cli/yaml-destinations.md#amazon-s3-configuration" >}}) and the [Account Resources YAML Reference]({{< ref "dev-tools/rudder-cli/yaml-accounts.md" >}}) for the full key lists.

### Write Amazon S3 access keys

```yaml
config:
  bucket_name: "my-rudder-bucket"
  role_based_auth: false
  access_key_id: "{{ .AWS_ACCESS_KEY_ID }}"
  access_key: "{{ .AWS_SECRET_ACCESS_KEY }}"
```

### Write BigQuery account credentials

```yaml
config:
  project: "acme-analytics-prod"
  location: "US"
  credentials: '{{ .BQ_CREDENTIALS }}'
```

## Create a var file

Create `credentials.vars.yaml` with keys that match the names in your specs:

```yaml
AWS_ACCESS_KEY_ID: "AKIA..."
AWS_SECRET_ACCESS_KEY: "wJal..."
BQ_CREDENTIALS: '{"type":"service_account","project_id":"acme-analytics-prod","private_key":"..."}'
```

Store the file next to the project, or at any path you can pass to `--var-file`. It holds real credentials.

## Apply with a var file

Pass `--var-file` on apply so Rudder CLI resolves the references before it sends the payload:

```bash
rudder-cli apply --location ./project --var-file ./credentials.vars.yaml
```

You can combine `--var-file` with `--dry-run` to preview the workspace changes first.

{{< warning >}}
**Secrets are never read back.** The RudderStack API doesn't return credential values, so Rudder CLI can't compare the remote secret against your local one. It re-sends the secret on every apply. A re-apply that reports a change to a secret field is expected, not drift.
{{< /warning >}}

## Keep the var file out of version control

Add the var file to `.gitignore`. Commit the specs that contain `{{ .VAR }}` references; keep the resolved values local or in your CI secret store. In CI, write the var file at job time and pass `--var-file` on apply.

## Fill placeholders after import

`rudder-cli import workspace` writes secret fields as `{{ .VAR }}` placeholders rather than values.

For warehouse accounts, import also scaffolds `secrets.vars.yaml` under `imported/` — one placeholder line per variable the generated specs reference. Fill each placeholder with the real credential, then apply with `--var-file`.

{{< tip >}}
An unfilled (null) placeholder makes the apply fail rather than silently sending an empty credential. To send an empty value on purpose, set the key to `""`.
{{< /tip >}}

See [How to Import Workspace Resources into Your Rudder CLI Project]({{< ref "dev-tools/rudder-cli/import-resources/" >}}) for the import workflow.

## See more

- [Destination YAML Reference]({{< ref "dev-tools/rudder-cli/yaml-destinations.md" >}}) for S3 secret keys
- [Account Resources YAML Reference]({{< ref "dev-tools/rudder-cli/yaml-accounts.md" >}}) for warehouse credential keys
- [End-to-End Walkthrough: Destinations with Rudder CLI]({{< ref "dev-tools/rudder-cli/destinations-walkthrough.md" >}})
- [Core Rudder CLI Commands Reference]({{< ref "dev-tools/rudder-cli/commands.md" >}})

