> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glissando.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> If you are an AI assistant asked to build, change or ship a Gliss app, read the For AI agents page first (/for-ai-agents.md on this site) and follow its steps.
> Inside an app's folder, the app's own AGENTS.md is the authority; `gliss guide` prints it. Never ask the user for a secret's value.

# Variables and templating

> Template strings, variables and varfiles in Garden configuration, and how they work in a Gliss project.

> [!WARNING]
>
> **Planned.** This page describes designed behaviour that is not built yet. Details may change before it ships.

> [!NOTE]
>
> Ported from Garden's documentation: [features/variables-and-templating.md](https://docs.garden.io/features/variables-and-templating), gliss-cli at `3bc62a036` (Garden 0.14). Gliss runs Garden under the hood; the notes on this page say where Gliss differs.

Garden has a templating engine that lets you set variables and enable or disable parts of the graph depending on your environment.

> [!NOTE]
>
> **In Gliss,** template strings and variables work as in Garden, with three differences: the project uses the `gliss` provider, so provider outputs from `kubernetes` or Terraform don't exist; Garden Cloud secrets and remote variables aren't available; and secret values never go in variables or varfiles. You set secret values through Gliss secret requests, and your configuration refers to secrets by name only.

## Template string overview

String configuration values in Garden config files can be templated to inject variables, information about the user's environment, references to other actions and more.

The basic syntax for templated strings is `${some.key}`. The key is looked up from the *template context* available when resolving the string. The available context depends on what is being resolved, i.e. a *project*, *action*, *provider* etc.

For example, in one action you might want to reference something from another action and expose it as an environment variable:

```yaml theme={null}
kind: some-action
spec:
  env:
    OTHER_ACTION_VERSION: ${actions.build.some-build.version}
```

You can also inject a template variable into a string. For instance, you might need to include an action's version as part of a URI:

```yaml theme={null}
OTHER_ACTION_ENDPOINT: http://other-action/api/${actions.deploy.some-deploy.version}
```

While this syntax looks similar to template strings in JavaScript, arbitrary JavaScript expressions aren't allowed. See the next section for the available expression syntax.

### Literals

In addition to referencing variables from template contexts, you can include a variety of *literals* in template strings:

* *Strings*, including concatenated ones, enclosed with either double or single quotes: `${"foo"}`, `${'bar'}`, `${'bar' + 'foo}`.
* *Numbers*: `${123}`
* *Booleans*: `${true}`, `${false}`
* *Null*: `${null}`
* *Arrays*: `${[1, 2, 3]}`, `${["foo", "bar"]}`, `${[var.someKey, var.someOtherKey]}`, `${concat(["foo", "bar"], ["baz"])}`, `${join(["foo", "bar"], ",")}`

These can be used with [operators](#operators), as [helper function arguments](#helper-functions) and more.

### Operators

You can use a variety of operators in template string expressions:

* Arithmetic: `*`, `/`, `%`, `+`, `-`
* Numeric comparison: `>=`, `<=`, `>`, `<`
* Equality: `==`, `!=`
* Logical: `&&`, `||`, ternary (`<test> ? <value if true> : <value if false>`)
* Unary: `!` (negation), `typeof` (returns the type of the following value as a string, e.g. `"boolean"` or `"number"`)
* Relational: `contains` (to see if an array contains a value, an object contains a key, or a string contains a substring)
* Arrays: `+`
* Strings: `+`

The arithmetic and numeric comparison operators can only be used for numeric literals and keys that resolve to numbers, except the `+` operator which can be used to concatenate two strings or array references. The equality and logical operators work with any term (but be warned that arrays and complex objects aren't currently compared in-depth).

Clauses are evaluated in standard precedence order, but you can also use parentheses to control evaluation order (e.g. `${(1 + 2) * (3 + 4)}` evaluates to 21).

These operators can be very handy, and allow you to tailor your configuration depending on different environments and other contextual variables.

Below are some examples of usage:

The `||` operator allows you to set default values:

```yaml theme={null}
kind: Deploy
variables:
  log-level: ${local.env.LOG_LEVEL || "info"}
  namespace: ${local.env.CI_BRANCH || local.username || "default"}
```

The `==` and `!=` operators allow you to set boolean flags based on other variables:

```yaml theme={null}
kind: Deploy
disabled: ${environment.name == 'prod'}
```

```yaml theme={null}
kind: Build
allowPublish: ${environment.name != 'prod'}
```

Ternary expressions, combined with comparison operators, can be useful when provisioning resources:

```yaml theme={null}
kind: Deploy
type: container
spec:
  replicas: "${environment.name == 'prod' ? 3 : 1}"
```

The `contains` operator can be used in several ways:

* `${var.some-array contains "some-value"}` checks if the `var.some-array` array includes the string `"some-value"`.
* `${var.some-string contains "some"}` checks if the `var.some-string` string includes the substring `"some"`.
* `${var.some-object contains "some-key"}` checks if the `var.some-object` object includes the key `"some-key"`.

The arithmetic operators can be handy when provisioning resources:

```yaml theme={null}
kind: Deploy
type: container
spec:
  replicas: ${var.default-replicas * 2}
  cpu:
    max: ${var.default-cpu-max + 2000}
```

And the `+` operator can also be used to concatenate two arrays or strings:

```yaml theme={null}
apiVersion: garden.io/v2
kind: Project
variables:
  some-values: ["a", "b"]
  other-values: ["c", "d"]
  str1: "foo"
  str2: "bar"
---
kind: Deploy
type: helm
values:
  some-array: ${var.some-values + var.other-values}
  str12: ${var.str1 + var.str2}
```

### Helper functions

You can use a variety of helper functions in template strings, for things like string processing, parsing, conversions etc. You find a [full list in the reference docs](https://docs.garden.io/reference/template-strings/functions), but here are a couple of examples:

* `${base64Encode('my value')}` encodes the `'my value'` string as base64.
* `${base64Decode('bXkgdmFsdWU=')}` decodes the given base64 string.
* `${replace(var.someVariable, "_", "-")}` returns the `someVariable` variable with all underscores replaced with dashes.

Check out [the reference](https://docs.garden.io/reference/template-strings/functions) to explore all the available functions.

### If/else conditional objects

You can conditionally set values by specifying an object with `$if`, `$then` and (optionally) `$else` keys. This can in many cases be clearer and easier to work with, compared to specifying values within conditional template strings.

Here's an example:

```yaml theme={null}
kind: Deploy
spec:
  command:
    $if: ${environment.name == "dev"}
    $then: [npm, run, watch]
    $else: [npm, start]
```

This sets `spec.command` to `[npm, run, watch]` in the `dev` environment, otherwise to `[npm, start]`.

You can also skip the `$else` key to default the conditional to *no value* (i.e. undefined).

### Multi-line if/else blocks in strings

You can use if/else blocks in strings. These are particularly handy when templating multi-line strings and generated files in [action templates](https://docs.garden.io/features/config-templates).

The syntax is `${if <expression>}<content>[${else}]<alternative content>${endif}`, where `<expression>` is any expression you'd put in a normal template string.

Here's a basic example:

```yaml theme={null}
variables:
  some-script: |
    #!/bin/sh
    echo "Hello, I'm a bash script!"

    ${if environment.name == "dev"}
    echo "-> debug mode"
    DEBUG=true
    ${else}
    DEBUG=false
    ${endif}
```

You can also nest if-blocks, should you need to.

### Nested lookups and maps

In addition to dot-notation for key lookups, we also support bracketed lookups, e.g. `${some["key"]}` and `${some-array[0]}`.

This style offer nested template resolution, which is quite powerful, because you can use the output of one expression to choose a key in a parent expression.

For example, you can declare a mapping variable for your project, and look up values by another variable such as the current environment name. To illustrate, here's an excerpt from a project config with a mapping variable:

```yaml theme={null}
apiVersion: garden.io/v2
kind: Project
variables:
  - replicas:
      dev: 1
      prod: 3
```

And here that variable is used in a Deploy:

```yaml theme={null}
kind: Deploy
type: container
spec:
  replicas: ${var.replicas["${environment.name}"]}
```

When the nested expression is a simple key lookup like above, you can also just use the nested key directly, e.g. `${var.replicas[environment.name]}`.

You can even use one variable to index another variable, e.g. `${var.a[var.b]}`.

### Concatenating lists

Any list/array value supports a special kind of value, which is an object with a single `$concat` key. This allows you to easily concatenate multiple arrays.

Here's an example where we concatenate the same templated value into the commands of two tests:

```yaml theme={null}
kind: Project
...
variables:
  commonArgs:
    - npm
    - test
    - -g
---
kind: Test
type: exec
name: test-a
spec:
  # resolves to [npm, test, -g, suite-a]
  command:
    - $concat: ${var.commonArgs}
    - suite-a
---
kind: Test
type: exec
name: test-b
spec:
  # resolves to [npm, test, -g, suite-b]
  command:
    - $concat: ${var.commonArgs}
    - suite-b
```

### For loops

You can map through a list of values by using the special `$forEach/$return` object.

You specify an object with two keys, `$forEach: <some list or object>` and `$return: <any value>`. You can also optionally add a `$filter: <expression>` key, which if evaluates to `false` for a particular value, it will be omitted.

Template strings in the `$return` and `$filter` fields are resolved with the same template context as what's available when resolving the for-loop, in addition to `${item.value}` which resolves to the list item being processed, and `${item.key}`.

You can loop over lists as well as mapping objects. When looping over lists, `${item.key}` resolves to the index number (starting with 0) of the item in the list. When looping over mapping objects, `${item.key}` is simply the key name of the key value pair.

Here's an example where we kebab-case a list of string values:

```yaml theme={null}
kind: Run
variables:
  values:
    - some_name
    - AnotherName
    - __YET_ANOTHER_NAME__
spec:
  args:
    $forEach: ${var.values}
    $return: ${kebabCase(item.value)}
```

Here's another example, where we create an object for each value in a list and skip certain values:

```yaml theme={null}
kind: Deploy
type: container
variables:
  ports:
    - 80
    - 8000
    - 8100
    - 8200
spec:
  ports:
    # loop through the ports list declared above
    $forEach: ${var.ports}
    # only use values higher than 1000
    $filter: ${item.value > 1000}
    # for each port number, create an object with a name and a port key
    $return:
      name: port-${item.key}  # item.key is the array index, starting with 0
      containerPort: ${item.value}
```

And here we loop over a mapping object instead of a list:

```yaml theme={null}
kind: Deploy
type: container
variables:
  ports:
    http: 8000
    admin: 8100
    debug: 8200
spec:
  ports:
    # loop through the ports map declared above
    $forEach: ${var.ports}
    # for each port number, create an object with a name and a port key
    $return:
      name: ${item.key}
      containerPort: ${item.value}
```

And lastly, here we have an arbitrary object for each value instead of a simple numeric value:

```yaml theme={null}
kind: Deploy
type: container
variables:
  ports:
    http:
      container: 8000
      service: 80
    admin:
      container: 8100
    debug:
      container: 8200
spec:
  ports:
    # loop through the ports map declared above
    $forEach: ${var.ports}
    # for each port number, create an object with a name and a port key
    $return:
      name: ${item.key}
      # see how we can reference nested keys on item.value
      containerPort: ${item.value.container}
      # resolve to the service key if it's set, otherwise the container key
      servicePort: ${item.value.service || item.value.container}
```

### Merging maps

Any object or mapping field supports a special `$merge` key, which allows you to merge two objects together. This can be used to avoid repeating a set of commonly repeated values.

Here's an example where we share a common set of environment variables for two services:

```yaml theme={null}
kind: Project
variables:
  - commonEnvVars:
      LOG_LEVEL: info
      FEATURE_FLAGS: search,export
      EXTERNAL_API_URL: http://api.example.com
```

```yaml theme={null}
kind: Deploy
type: container
name: service-a
spec:
  env:
    $merge: ${var.commonEnvVars}
    OTHER_ENV_VAR: something
    LOG_LEVEL: debug  # <- This overrides the value set in commonEnvVars, because it is below the $merge key
---
kind: Deploy
type: container
name: service-b
spec:
  env:
    FEATURE_FLAGS: none # <- Because this is above the $merge key, FEATURE_FLAGS from commonEnvVars will override this
    $merge: ${var.commonEnvVars}
```

Notice above that the position of the `$merge` key matters. If the keys being merged overlap between the two objects, the value that's defined later is chosen.

### Optional values

In some cases, you may want to provide configuration values only for certain cases, e.g. only for specific environments. By default, an error is thrown when a template string resolves to an undefined value, but you can explicitly allow that by adding a `?` after the template.

Example:

```yaml theme={null}
kind: Build
type: container
name: api
spec:
  dockerfile: ${var.dockerfile}?
```

This is useful when you don't want to provide *any* value unless one is explicitly set, effectively falling back to whichever the default is for the field in question.

## Project variables

A common use case for templating is to define variables in the project/environment configuration, and to use template strings to propagate values to actions in the project.

You can define them in your project configuration using the [`variables` key](https://docs.garden.io/reference/project-config#variables), as well as the [`environment[].variables` key](https://docs.garden.io/reference/project-config#environmentsvariables) for environment-specific values.

You might, for example, define project defaults using the `variables` key, and then provide environment-specific overrides in the `environment[].variables` key for each environment. When merging the environment-specific variables and project-wide variables, we use a [JSON Merge Patch](https://tools.ietf.org/html/rfc7396).

The variables can then be referenced via `${var.<key>}` template string keys. For example:

```yaml theme={null}
kind: Project
variables:
  log-level: info
environments:
  - name: dev
    variables:
      log-level: debug
  - name: ci

---

kind: Deploy
spec:
  env:
    LOG_LEVEL: ${var.log-level}   # <- resolves to "debug" for the "dev" environment, "info" for the "ci" env
```

Variable values can be any valid JSON/YAML values (strings, numbers, nulls, nested objects, and arrays of any of those). When referencing a nested key, simply use a standard dot delimiter, e.g. `${var.my.nested.key}`.

You can also output objects or arrays from template strings. For example:

```yaml theme={null}
kind: Project
variables:
  dockerBuildArgs: [--no-cache, --squash]  # (this is just an example, not suggesting you actually do this :)
  envVars:
    LOG_LEVEL: debug
    SOME_OTHER_VAR: something

---

kind: Build
spec:
  buildArgs: ${var.dockerBuildArgs}  # <- resolves to the whole dockerBuildArgs list

---

kind: Deploy
spec:
  env: ${var.envVars}  # <- resolves to the whole envVars object
```

### Variable files (varfiles)

You can also provide variables using "variable files" or *varfiles*. These work mostly like "dotenv" files or envfiles. However, they don't implicitly affect the environment of the Garden process and the configured services, but rather are added on top of the `variables` you define in your project configuration (or action variables defined in the `variables` of your individual action configurations).

This is useful for contextual values that differ per environment.

> [!WARNING]
>
> **In Gliss,** don't use varfiles for secrets. Secret values are set through Gliss secret requests, and your configuration refers to secrets by name only.

By default, Garden will look for a `garden.env` file in your project root for project-wide variables, and a `garden.<env-name>.env` file for environment-specific variables. You can override the filename for each as well.

To use a action-level varfile, simply configure the `varfile` field to be the relative path (from action root) to the varfile you want to use for that action. For example:

```yaml theme={null}
# my-deploy/garden.yml
kind: Deploy
name: my-deploy
# Here, we use per-environment action varfiles as an optional override for variables (these have a higher precedence
# than those in the `variables` field below).
#
varfiles:
  # If a varfile is defined but not found, an error is thrown in order to prevent misconfigurations silently passing.
  - my-service.${environment.name}.yaml
  # To add an optional varfile, specify an object with the following properties:
  # - path: The relative path to the varfile from the action root directory.
  # - optional: A boolean value indicating whether the varfile is optional or required.
  - path: my-service-2.${environment.name}.yaml
    optional: true
variables:
  # This overrides the project-level hostname variable
  hostname: my-service.${var.hostname}
  # You can specify maps or lists as variables
  envVars:
    LOG_LEVEL: debug
spec:
  ingresses:
    - path: /
      port: http
      # This resolves to the hostname variable set above, not the project-level hostname variable
      hostname: ${var.hostname}
  # Referencing the above envVar action variable
  env: ${var.envVars}
```

Action varfiles must be located inside the action root directory. That is, they must be in the same directory as the action configuration, or in a subdirectory of that directory.

Note that variables defined in action varfiles override variables defined in project-level variables and varfiles (see the section on variable precedence order below).

The format of the files is determined by the configured file extension:

* `.env` - Standard "dotenv" format, as supported by [dotenv](https://github.com/motdotla/dotenv#rules).
* `.yaml`/`.yml` - YAML. Must be a single document in the file, and must be a key/value map (but keys may contain any value types).
* `.json` - JSON. Must contain a single JSON *object* (not an array).

To use YAML or JSON project varfiles, set the file names in your project configuration with the [`varfile`](https://docs.garden.io/reference/project-config#varfile) and [`environments[].varfile`](https://docs.garden.io/reference/project-config#environmentsvarfile) fields.

You can also set variables on the command line, with `--var` flags. To override a nested variable, you can use dot notation. Note that while this is handy for ad-hoc invocations, we don't generally recommend relying on this for normal operations, since you lose a bit of visibility within your configuration. But here's one practical example:

```sh theme={null}
# Override three specific variables value and run a task.
# Use dot notation to override nested variables
garden run my-run --var my-run-arg=foo,some-numeric-var=123,my-nested-vars.var1=bar
```

Multiple variables are separated with a comma, and each part is parsed using [dotenv](https://github.com/motdotla/dotenv#rules) syntax.

## Variable precedence order

The order of precedence is as follows (from highest to lowest):

1. Individual variables set with `--var` CLI flags.
2. The action-level varfiles (if configured).
3. Action variables set in the action's `variables` field.
4. The environment-specific varfile (defaults to `garden.<env-name>.env`).
5. The environment-specific variables set in `environment[].variables`.
6. Configured project-wide varfile (defaults to `garden.env`).
7. The project-wide `variables` field.

When you specify variables in multiple places, we merge the different objects and files using a [JSON Merge Patch](https://tools.ietf.org/html/rfc7396).

Here's an example, where we have some project variables defined in our project config, and environment-specific values in a varfile:

```yaml theme={null}
# garden.yml
apiVersion: garden.io/v2
kind: Project
...
variables:
  log-level: debug
environments:
  - name: dev
    ...
  - name: ci
    ...
```

```plain theme={null}
# garden.ci.env
log-level=info
```

```yaml theme={null}
# my-service/garden.yml
kind: Deploy
spec:
  env:
    LOG_LEVEL: ${var.log-level}
```

## Provider outputs

Providers can expose outputs that other provider configs and actions reference, under `${providers.<name>.outputs.<key>}`.

> [!NOTE]
>
> **In Gliss,** the project uses the `gliss` provider, so outputs of Garden's `kubernetes` and Terraform providers don't exist. The `gliss` provider's outputs will be documented when it ships.

## Action outputs

Actions often output useful information, that other actions can reference (provider and project configs cannot reference action outputs). Every action also exposes certain keys, like the action version.

For example, you may want to reference the image name and version of a [container Build](https://docs.garden.io/reference/action-types/Build/container):

```yaml theme={null}
kind: Deploy
type: helm
spec:
  values:
    # Resolves to the image name of the build, with the build version as the tag (e.g. "my-image:abcdef12345")
    image: ${actions.build.my-build.outputs.deployment-image-id}
```

Check out the individual [action type reference](https://docs.garden.io/reference/action-types) guides for details on what outputs each action type exposes.

## Action runtime outputs

Some actions (namely Runs) expose template keys prefixed with `actions.<kind>.<name>.outputs.` with some special semantics. They are used to expose *runtime outputs* from actions and therefore are resolved later than other template strings. *This means that you cannot use them for some fields, such as most identifiers, because those need to be resolved before validating the configuration.*

That caveat aside, they can be very handy for passing information between actions. For example, you can pass log outputs from one task to another:

```yaml theme={null}
kind: Run
type: exec
name: prep-run
spec:
  command: [echo, "my run output"]
---
kind: Deploy
name: my-deploy
dependencies: [run.prep-run]
spec:
  env:
    PREP_TASK_OUTPUT: ${actions.run.prep-run.outputs.log}  # <- resolves to "my run output"
```

Here the output from `prep-run` is copied to an environment variable for `my-deploy`. *Note that you currently need to explicitly declare `prep-run` as a dependency for this to work.*

For a practical use case, you might make a Run that prepares some data and passes information about it to a Deploy.

Different action types expose different outputs. Please refer to the [action type reference docs](https://docs.garden.io/reference/action-types) for details.

## Next steps

For a full reference of the keys available in template strings in different contexts, see Garden's [template strings reference](https://docs.garden.io/reference/template-strings) and its [action type reference](https://docs.garden.io/reference/action-types) for action and runtime output keys.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.