envsubst — Substitute Environment Variables in Text

Practical guide to envsubst (GNU gettext): substitute environment variables in templates and text files — safely, without shell expansion.

envsubst is part of GNU gettext and replaces every $VAR and ${VAR} reference in a text with the value of the matching environment variable. Unlike eval or a shell, it performs no shell expansion – no command substitution, no globbing, no quoting tricks – it only swaps in variable values. That is exactly what makes it a safe tool for filling templates such as nginx configurations, .env files or Kubernetes manifests with values from the environment. Pass an argument like '$VAR1 $VAR2' to restrict substitution to specific variables, so other placeholders (for example nginx's own $uri) stay untouched. This guide walks you through the invocations you actually reach for, from Docker entrypoints to CI/CD pipelines.

Basic Usage

envsubst < <template> > <output> — Replace all $VAR and ${VAR} references with their values.

envsubst < config.template > config.conf

echo '$HOME' | envsubst — Substitute variables in piped text.

echo 'Hello $USER, your home is $HOME' | envsubst

envsubst < <template> — Output substituted content to stdout.

envsubst < nginx.conf.template

cat <template> | envsubst > <output> — Pipe template through envsubst.

cat docker-compose.yml.tmpl | envsubst > docker-compose.yml

Restrict Variables

envsubst '$VAR1 $VAR2' < <template> — Only substitute specific variables (leave others as-is).

envsubst '$DB_HOST $DB_PORT' < config.template > config.conf

envsubst '${VAR1}' < <template> — Substitute only one specific variable.

envsubst '${APP_VERSION}' < version.txt.tmpl > version.txt

envsubst --variables '<shell-format>' — List all variable names referenced in the argument.

envsubst --variables "$(cat nginx.conf.template)"

Docker & Container Usage

envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf — Template an NGINX config in a Docker entrypoint.

envsubst '${NGINX_HOST} ${NGINX_PORT}' < default.conf.template > /etc/nginx/conf.d/default.conf

envsubst < app.env.template > .env — Generate a .env file from a template during deployment.

envsubst < app.env.template > .env

envsubst '$VAR' < template — Substitute only selected variables; all other ${...} references stay literal.

echo 'Home: $HOME, keep: ${BUILD_ID}' | envsubst '$HOME'

CI/CD & Scripting

export VAR=value && envsubst < template > output — Set a variable and substitute in one command chain.

export VERSION=1.2.3 && envsubst < manifest.yml.tmpl > manifest.yml

VAR=value envsubst < template — Inline variable definition for the envsubst call.

APP_NAME=myapp envsubst < deploy.yaml.tmpl

env $(cat .env | xargs) envsubst < template — Load variables from a .env file and substitute.

env $(cat .env | xargs) envsubst < config.template > config.conf

envsubst < k8s/deployment.yaml.tmpl | kubectl apply -f - — Template a Kubernetes manifest and apply it directly.

export IMAGE_TAG=v2.0 && envsubst < k8s/deployment.yaml.tmpl | kubectl apply -f -

Common Patterns

for f in *.tmpl; do envsubst < "$f" > "${f%.tmpl}"; done — Process all template files and drop the .tmpl extension.

for f in conf/*.tmpl; do envsubst < "$f" > "${f%.tmpl}"; done

envsubst --variables "$(cat template)" | sort — List all variables a template requires.

envsubst --variables "$(cat nginx.conf.template)" | sort

envsubst < template | diff template - — Show what was substituted in a template.

envsubst < config.template | diff config.template -

Conclusion

The strength of envsubst is its safe, tightly scoped substitution: it only swaps $VAR/${VAR} and, unlike eval or a shell, runs no commands at all – ideal for filling untrusted templates. When you pass a shell-format string such as '$DB_HOST $DB_PORT', envsubst restricts substitution to exactly those variables; every other placeholder (for example nginx's own $uri) is left verbatim. Watch out for one pitfall, though: an unset variable is silently replaced with an empty string rather than reported as an error – use envsubst --variables to check that every required value is present in the environment before you rely on the output. envsubst has no $$ escape for a literal dollar sign; to keep a reference verbatim, simply leave it out of the list of variables to substitute.

Further Reading

  • sed – stream editor for line-by-line search and replace in text streams
  • tr – translate or delete individual characters in a text stream
  • tee – write a data stream to a file and stdout at the same time