${...} substitution
Any string value in runwisp.toml can take its value from the daemon’s
environment or from a file:
[tasks.backup]cron = "${BACKUP_CRON}" # from the daemon's environmentdescription = "backs up ${REGION}" # works inside a longer stringrun = "/usr/local/bin/backup.sh"
[notifiers.slack-ops]type = "slack"webhook_url = "${file:secrets/slack.url}" # from a fileIt works on every string value (cron, paths, env values, notifier credentials,
compose blocks) except run. Use it to keep credentials
out of the TOML file.
${VAR}: environment variables
Section titled “${VAR}: environment variables”${VAR} is replaced with VAR from the daemon’s environment (the shell,
systemd unit, or container that started runwisp daemon).
-
An unset variable is a config-load error that names the variable and the key:
tasks.backup.cron: environment variable BACKUP_CRON is not set -
A variable that is set but empty gives an empty string.
${file:path}: file contents
Section titled “${file:path}: file contents”${file:path} is replaced with the file’s contents, with leading and trailing
whitespace removed (so a trailing newline doesn’t break a token).
- A missing or unreadable file is a config-load error.
- Paths can be absolute,
~/...(your home directory), or relative to the directory ofrunwisp.toml. - Works well with files written by a secrets tool (Vault agent, sops, Docker
secrets in
/run/secrets/...). Set the file tochmod 600.
When it runs
Section titled “When it runs”Substitution runs once, when the config is loaded: at startup and on
runwisp reload. The daemon doesn’t watch the
environment or the files. After a change, reload or restart.
The exception: run
Section titled “The exception: run”run (on tasks and services) is never
substituted. The shell expands ${VAR} in it at run time, using the full process
environment, including env,
env_file,
secrets, and
secrets_file:
[tasks.backup]run = "backup.sh --bucket ${BACKUP_BUCKET}" # the shell expands this
[tasks.backup.env]BACKUP_BUCKET = "s3://prod-backups"What else is not substituted
Section titled “What else is not substituted”- Keys. Env var names, task names, and compose service names stay as written. Only values are substituted.
- Referenced files. Dotenv files (
env_file,secrets_file) and compose files (compose_file) are read as they are. - Crontabs read through
include_cron.
Escaping
Section titled “Escaping”Write $${ for a literal ${:
description = "template is $${VAR}" # → template is ${VAR}- A single
$(likecost: $5) needs no escaping. - An unclosed
${(no}) is a config-load error. runwisp importescapes${for you, because cron and supervisord don’t substitute. Imported text keeps its meaning. To make an imported value substitute, remove the extra$.
Worked example
Section titled “Worked example”[tasks.export]cron = "${EXPORT_CRON}"run = "/usr/local/bin/export"
[tasks.export.secrets]API_TOKEN = "${file:secrets/export.token}"
[notifiers.slack-ops]type = "slack"webhook_url = "${SLACK_OPS_WEBHOOK}"
[notifiers.tg-oncall]type = "telegram"bot_token = "${file:~/.config/runwisp/tg.token}"chat_id = "-1001234567890"Start the daemon with EXPORT_CRON and SLACK_OPS_WEBHOOK set and the token
files at 0600. The TOML file then contains no secrets.