Skip to content

Variables

Holden uses a simple variable syntax to inject configuration, secrets, and auto-generated values into your containers.

Variables use the ${...} syntax and support string interpolation:

env:
# Single variable
LOG_LEVEL: ${config.LOG_LEVEL}
# Multiple variables in one value
API_URL: https://${config.HOST}:${config.PORT}/api
# Mixed with literal text
GREETING: "Hello, ${config.USERNAME}!"

Variable names are case-sensitive. ${config.API_KEY} and ${config.api_key} are different variables.

SyntaxSourceExample
${config.X}Plain value from holden.vars.yml${config.LOG_LEVEL}
${secret.X}Encrypted value from holden.vars.yml${secret.API_KEY}
${needs.X.Y}Auto-generated by needs${needs.postgres.url}

Plain configuration values. Stored as-is in holden.vars.yml, visible in git.

holden.vars.yml
values:
LOG_LEVEL: debug
CONTACT_EMAIL: [email protected]
holden.yml
services:
web:
env:
LOG_LEVEL: ${config.LOG_LEVEL}
CONTACT_EMAIL: ${config.CONTACT_EMAIL}

Use for non-sensitive configuration: log levels, feature flags, email addresses.

Encrypted values. The secure: wrapper contains age-encrypted ciphertext.

holden.vars.yml
values:
API_KEY:
secure: YWdlLWVuY3J5cHRpb24ub3Jn...
holden.yml
services:
web:
env:
API_KEY: ${secret.API_KEY}

Holden decrypts these at deploy time. The plaintext value is injected into the container’s environment.

Auto-generated values for managed infrastructure. The syntax is ${needs.<service>.<variable>}.

env:
DATABASE_URL: ${needs.postgres.url}
REDIS_URL: ${needs.valkey.url}
S3_ENDPOINT: ${needs.garage.url}

See the individual needs pages for available variables:

Terminal window
holden vars set LOG_LEVEL debug
holden vars set API_KEY --secret
holden vars get LOG_LEVEL

See CLI Reference for complete command documentation.

If a variable doesn’t exist, Holden fails the deploy with an error:

Undefined config variable: MISSING_VAR