This page covers the variable and output blocks: how a variable is
declared and read, where its value comes from and which source wins, and how
an output is declared and read back from the application.
Declaring variables
variable "region" { default = "eu" description = "The region the application runs in"}
app "web" { image = "nginx:1.27" region = variable.region}A variable block takes a default, which is required, and an optional
description. Configuration reads the variable as variable.<name>, here
variable.region. A variable’s value is any value an attribute can hold: a
string, a number, a bool, a list or a map.
Defaults built from other values
variable "url" { default = "http://${app.web.host}"}
app "web" { image = "nginx:1.27" host = "web.internal"}A default is an expression like any other attribute value, so it may hold
references to other entities and function calls, not only literals. This is
where a derived value lives: declare a variable whose default is built from
the values it depends on, and read it as variable.url wherever it is needed.
The references are followed when the variable is evaluated, so variable.url
above is http://web.internal.
Setting variables from the application
The application sets a variable when it creates the Config. A value given
here overrides every other source:
local := registry.NewLocal()registry.Register[App](local, "app")
c, err := xcl.NewConfig( xcl.WithRegistry(local), xcl.WithVariables(map[string]any{"region": "us"}),)if err != nil { return err}local = xcl.LocalRegistry()local.register_type(App, "app")
with xcl.Config(registries=[local], variables={"region": "us"}) as cfg: cfg.apply("./config").vars files
region = "us"replicas = 3A .vars file holds attributes only, one per variable, and no blocks. Every
file ending in .vars in the directory of each path given to an operation is
read; a path that is a file is read from its own directory. The search is not
recursive, and there is no option to name a .vars file: a file is read
because of where it is. A .vars file that is not well formed fails the
operation, reported like any other
problem in configuration.
Environment variables
export XCL_VAR_region=usAn environment variable named XCL_VAR_<name> sets the variable <name>.
Only variables with the XCL_VAR_ prefix are read; any other environment
variable is ignored, and configuration that needs one reads it with the
env function instead. See Functions.
Precedence
A variable takes its value from the last of these sources that sets it, each overriding the one before:
- the variable’s
default .varsfilesXCL_VAR_environment variables- values given by the application, with
xcl.WithVariablesorvariables=
Outputs
app "web" { image = "nginx:1.27" host = "web.internal" port = 80}
app "api" { image = "api:1.4" host = "api.internal" port = 8080}
output "url" { value = "http://${app.web.host}" description = "The address the web application is reached at"}
output "hosts" { value = [app.web.host, app.api.host]}
output "ports" { value = { web = app.web.port api = app.api.port }}An output block publishes a value, with an optional description. The
value is any expression, so an output can publish a string, a list or a map
built from other entities. The application reads an output back by its
address, output.url, as the next section shows.
A module’s outputs are read from its parent as module.<name>.output.<name>,
and they are the only way to reach inside the module; the parts of a list or
map output are read by index or key, module.db.output.hosts[0] and
module.db.output.ports.web. See Modules.
Reading an output from another block in the same configuration, as
output.url, is not yet supported. A value several blocks share belongs in a
variable whose default builds it; see
Defaults built from other values.
Reading outputs from the application
Once the configuration is applied, Outputs returns every published value
keyed by address, including the outputs of modules at any depth, such as
module.db.output.port. In Go, Find with types.Output returns the output
entity itself, and its Value holds the published value:
if err := c.Apply("./config"); err != nil { return err}
outputs := c.Outputs() // map[string]any, keyed by addressfmt.Println(outputs["output.url"])
url, err := xcl.Find[types.Output](c, "output.url")if err != nil { return err}fmt.Println(url.Value)cfg.apply("./config")
outputs = cfg.outputs() # dict keyed by addressprint(outputs["output.url"])A list output is read back as a Go []any or a Python list, and a map
output as a map[string]any or a dict. A sensitive part of an output keeps
its protection when read back; see Sensitive values.
Next
- Modules: group configuration into a directory with its own variables and outputs.
- References: how
variable.regionandmodule.db.output.portreach other blocks. - Functions: the functions a default or an output value can call.