Modern DevOps workflows, Docker container deployments, and CI/CD automation pipelines rely on parameterized configuration files. For example, an Nginx reverse proxy configuration, Kubernetes deployment manifest, or database connection file often needs to ingest dynamic hostnames, port numbers, API tokens, and environment identifiers at deploy time without hardcoding credentials into Git repositories.
While complex templating engines like Jinja2, Helm, or custom Python scripts can handle variable substitution, Linux includes a blazingly fast, lightweight, and native command-line utility built specifically for this purpose: envsubst. Part of the standard GNU gettext utilities suite, envsubst reads a template from standard input, substitutes occurrences of shell environment variables with their active values, and streams the rendered result to standard output.
This guide explains how envsubst operates, its syntax nuances, how to avoid accidentally overwriting unintended variables, and how to integrate it into real-world Linux container workflows.
How envsubst Works
The basic execution model of envsubst follows standard Unix stream processing:
envsubst < template.conf > output.conf
Whenever envsubst encounters an environment variable written in standard shell syntax—such as $VARIABLE_NAME or ${VARIABLE_NAME}—it replaces that token with the current value of the exported environment variable. If an environment variable is unset or empty, envsubst replaces it with an empty string.
Installing envsubst on Linux
envsubst is distributed as part of the gettext package. It is pre-installed on most standard Linux distributions, but can be added to minimal container images easily:
On Debian / Ubuntu:
sudo apt update && sudo apt install -y gettext-base
On Alpine Linux (Common for Docker):
apk add --no-cache gettext
On RHEL / Rocky Linux / Fedora:
sudo dnf install -y gettext
The Critical Pitfall: Overwriting Unintended Variables
By default, if you run raw envsubst on a configuration file, it replaces every single string that looks like a shell variable ($var). In configuration files that use dollar signs for internal application syntax—such as Nginx (which uses $host, $uri, $remote_addr) or shell scripts—raw envsubst will strip and erase all those internal variables, breaking your configuration completely!
The Solution: Variable Whitelisting
To substitute only specific variables while leaving all other dollar-sign variables intact, pass a comma- or space-separated list of target variable names as a command argument:
envsubst '$PORT,$BACKEND_URL' < nginx.conf.template > nginx.conf
Notice the single quotes and backslashes: escaping the dollar signs prevents your active interactive shell from expanding the variable names before passing them to envsubst.
Real-World Example: Parameterizing an Nginx Configuration
1. Create the Template File (nginx.conf.template)
server {
listen ${SERVER_PORT};
server_name ${DOMAIN_NAME};
location / {
proxy_pass http://${BACKEND_HOST}:${BACKEND_PORT};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
2. Define Environment Variables
export SERVER_PORT="80"
export DOMAIN_NAME="api.example.com"
export BACKEND_HOST="127.0.0.1"
export BACKEND_PORT="8080"
3. Render with Targeted Substitution
Execute envsubst, specifying strictly the variables you want replaced:
envsubst '$SERVER_PORT $DOMAIN_NAME $BACKEND_HOST $BACKEND_PORT' < nginx.conf.template > /etc/nginx/conf.d/default.conf
4. Review the Rendered Output
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Notice that while ${SERVER_PORT} and ${BACKEND_HOST} were replaced with their environment values, Nginx’s internal $host, $remote_addr, and $proxy_add_x_forwarded_for were perfectly preserved.
Advanced Techniques: Dynamic Whitelisting in Shell Scripts
If your template contains dozens of environment variables, manually typing every variable into the whitelist argument is tedious. You can combine envsubst with its -v (variables) flag to automatically extract and whitelist all variables defined in the template:
# Automatically substitute only variables that are actually defined in the current shell environment
DEFINED_ENVS=$(printf '${%s} ' $(env | cut -d= -f1))
envsubst "$DEFINED_ENVS" < template.yaml > output.yaml
Command Reference Summary
| Syntax Pattern | Behavior | Best Used For |
|---|---|---|
envsubst < in > out |
Replaces all $VAR strings globally |
Plain text files, SQL scripts, YAML without native $ syntax |
envsubst '$VAR1,$VAR2' < in > out |
Replaces strictly VAR1 and VAR2 |
Nginx configurations, Apache configs, Bash scripts |
envsubst -v < in |
Lists all variable names found in the template | Auditing required variables before deployment |
By incorporating envsubst into your container startup entrypoint scripts (e.g., /docker-entrypoint.d/), developers achieve elegant, zero-overhead configuration management across all Linux environments.