Creación de plantillas de configuración con envsubst y heredocs
Genere la configuración en tiempo de ejecución a partir de variables de entorno mediante envsubst y heredocs entrecomillados.
Creación de plantillas de configuración con envsubst y heredocs es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
Por qué es importante generar plantillas de configuración en tiempo de ejecución
En los flujos de trabajo de DevOps y de contenedores, los archivos de configuración como nginx.conf, prometheus.yml y docker-compose.yml suelen tener que cambiar entre entornos: staging, producción y recuperación ante desastres (DR). Codificar los valores directamente provoca divergencias y expone secretos.
La solución es la generación de plantillas de configuración en tiempo de ejecución: incluya una plantilla con marcadores y, después, inyecte los valores reales al iniciarse el contenedor a partir de variables de entorno. Así, la imagen permanece inmutable y la configuración se puede auditar.
- No se incrustan secretos en las imágenes
- El mismo artefacto se promueve entre entornos
- La configuración se genera justo antes de iniciar el proceso
Dos herramientas complementarias hacen que esto sea muy sencillo en Bash: envsubst y los heredocs entrecomillados.
envsubst: el generador de configuración de una sola línea
envsubst es una pequeña utilidad de GNU que lee de stdin, sustituye los marcadores $VARIABLE y ${VARIABLE} por sus valores del entorno actual y escribe el resultado en stdout.
Se incluye en el paquete gettext y está disponible prácticamente en todas las distribuciones de Linux y en las imágenes base de Docker.
- Funciona con cualquier formato de texto: NGINX, YAML, TOML, JSON e INI
- No evalúa la sintaxis del shell; solo sustituye referencias a variables
- Es segura: no ejecuta comandos incluidos en la plantilla
#!/usr/bin/env bash
# Install check (usually already present)
which envsubst || apt-get install -y gettext-base
# Minimal demo
export APP_PORT=8080
export APP_HOST=api.example.com
echo 'server { listen ${APP_PORT}; server_name ${APP_HOST}; }' | envsubst
# Output: server { listen 8080; server_name api.example.com; }Sustitución selectiva de variables
De forma predeterminada, envsubst sustituye todos los $VAR que encuentra. Esto puede sobrescribir variables de NGINX como $uri o $host; son directivas reales de NGINX, no variables de entorno.
Pase una lista explícita de variables como primer argumento para limitar la sustitución únicamente a esos nombres:
envsubst '$VAR1 $VAR2'El argumento es una cadena entre comillas simples (para que el shell no la expanda) que contiene los nombres de las variables que desea sustituir, separados por espacios o saltos de línea.
#!/usr/bin/env bash
export APP_PORT=8080
export APP_HOST=api.example.com
# NGINX template contains both our vars AND nginx vars ($uri, $host)
TEMPLATE='server {
listen ${APP_PORT};
server_name ${APP_HOST};
location / {
proxy_set_header Host $host;
proxy_pass http://backend$uri;
}
}'
# Only substitute APP_PORT and APP_HOST — leave $host and $uri untouched
echo "$TEMPLATE" | envsubst '${APP_PORT} ${APP_HOST}'
Archivos de plantilla en disco
Para configuraciones reales, almacene la plantilla en un archivo (por ejemplo, nginx.conf.template) junto al Dockerfile. Al iniciarse el contenedor, ejecute envsubst para generar el archivo de configuración final antes de iniciar el demonio.
Este es el patrón canónico utilizado por la imagen oficial de Docker de NGINX.
#!/usr/bin/env bash
# File: nginx.conf.template
# (In practice this lives on disk; we write it here for demo purposes)
cat > /tmp/nginx.conf.template << 'TMPL'
server {
listen ${NGINX_PORT};
server_name ${SERVER_NAME};
root /var/www/${APP_ENV};
location / {
proxy_pass http://app:${APP_PORT};
}
}
TMPL
export NGINX_PORT=80
export SERVER_NAME=myapp.example.com
export APP_ENV=production
export APP_PORT=3000
# Generate final config
envsubst '${NGINX_PORT} ${SERVER_NAME} ${APP_ENV} ${APP_PORT}' \
< /tmp/nginx.conf.template \
> /tmp/nginx.conf
cat /tmp/nginx.confHeredocs entrecomillados: plantillas en línea sin archivos temporales
Un heredoc entrecomillado (mediante << 'EOF', con comillas simples alrededor del delimitador) impide que el shell expanda variables o ejecute sustituciones de comandos dentro del bloque. El contenido se trata como una cadena literal.
Esto convierte los heredocs en la forma perfecta de escribir una plantilla en línea y enviarla directamente a envsubst, sin necesidad de usar un archivo intermedio.
<< EOF(sin comillas): el shell expande$VARinmediatamente<< 'EOF'(entrecomillado): el contenido es literal; la expansión se pospone hastaenvsubst
#!/usr/bin/env bash
export DB_HOST=postgres.internal
export DB_PORT=5432
export DB_NAME=myapp_prod
# Quoted heredoc: shell does NOT expand $DB_HOST etc. yet
envsubst << 'EOF'
[database]
host = ${DB_HOST}
port = ${DB_PORT}
dbname = ${DB_NAME}
EOF
# Output uses actual env var values — expansion done by envsubst, not the shellCombinar heredocs con la redirección de salida
Envíe un heredoc entrecomillado a través de envsubst y redirija el resultado a un archivo en una sola expresión. Es el patrón idiomático más limpio para generar archivos de configuración en un script de entrada.
Use la sustitución selectiva ('${VAR1} ${VAR2}') cuando el formato de destino (Prometheus, NGINX, etc.) tenga su propia sintaxis $variable que deba protegerse.
#!/usr/bin/env bash
# entrypoint.sh — Docker container entrypoint
set -euo pipefail
export PROM_PORT=${PROM_PORT:-9090}
export SCRAPE_INTERVAL=${SCRAPE_INTERVAL:-15s}
export TARGET_HOST=${TARGET_HOST:-localhost:8080}
envsubst '${PROM_PORT} ${SCRAPE_INTERVAL} ${TARGET_HOST}' << 'EOF' > /etc/prometheus/prometheus.yml
global:
scrape_interval: ${SCRAPE_INTERVAL}
evaluation_interval: ${SCRAPE_INTERVAL}
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['${TARGET_HOST}']
EOF
echo "[entrypoint] Prometheus config written on port ${PROM_PORT}"
exec prometheus --config.file=/etc/prometheus/prometheus.yml --web.listen-address=":${PROM_PORT}"Valores predeterminados y validación antes de la sustitución
No dé por hecho que todas las variables necesarias están definidas. Use la expansión de parámetros de Bash para proporcionar valores predeterminados o terminar con un error explícito:
${VAR:-default}: usadefaultsiVARno está definida o está vacía${VAR:?error message}: termina con un error siVARno está definida o está vacía
Defina estos valores antes de llamar a envsubst para que la plantilla siempre reciba un valor concreto o el script se detenga pronto con un mensaje útil.
#!/usr/bin/env bash
set -euo pipefail
# Required — abort if missing
: "${DATABASE_URL:?DATABASE_URL must be set}"
: "${SECRET_KEY:?SECRET_KEY must be set}"
# Optional with defaults
export APP_PORT=${APP_PORT:-8000}
export LOG_LEVEL=${LOG_LEVEL:-info}
export WORKERS=${WORKERS:-4}
envsubst '${DATABASE_URL} ${SECRET_KEY} ${APP_PORT} ${LOG_LEVEL} ${WORKERS}' \
< /app/config/app.conf.template \
> /app/config/app.conf
echo "[init] Config generated — port=${APP_PORT} workers=${WORKERS} log=${LOG_LEVEL}"Generar configuraciones con varias secciones mediante múltiples heredocs
Para configuraciones complejas compuestas por secciones lógicas, puede generar cada sección de forma independiente y concatenarlas, o usar un único heredoc que abarque todo el archivo. Ambos enfoques funcionan; elija según la legibilidad.
Cuando las secciones se incluyen de forma condicional (por ejemplo, el bloque TLS solo si se ha definido una ruta al certificado), el enfoque de múltiples heredocs con bloques if resulta más claro.
#!/usr/bin/env bash
set -euo pipefail
export APP_HOST=${APP_HOST:-localhost}
export APP_PORT=${APP_PORT:-8080}
export TLS_CERT=${TLS_CERT:-}
export TLS_KEY=${TLS_KEY:-}
CONFIG_FILE=/tmp/app.conf
# Base section
envsubst '${APP_HOST} ${APP_PORT}' << 'BASE' > "$CONFIG_FILE"
[server]
host = ${APP_HOST}
port = ${APP_PORT}
BASE
# Conditional TLS section — only appended when cert is provided
if [[ -n "$TLS_CERT" && -n "$TLS_KEY" ]]; then
envsubst '${TLS_CERT} ${TLS_KEY}' << 'TLS' >> "$CONFIG_FILE"
[tls]
cert_file = ${TLS_CERT}
key_file = ${TLS_KEY}
TLS
echo "[init] TLS enabled"
else
echo "[init] TLS disabled (no cert/key provided)"
fi
cat "$CONFIG_FILE"Patrón de entrada de Docker
El patrón recomendado para la entrada de Docker usa un script de shell (docker-entrypoint.sh) que genera las configuraciones al iniciar y luego cede el control al proceso principal mediante exec. El uso de exec reemplaza el proceso del shell por el daemon, por lo que las señales (SIGTERM, SIGINT) llegan directamente al daemon, algo fundamental para un apagado ordenado.
Los archivos de plantilla se agregan a la imagen durante la compilación; los valores se inyectan en tiempo de ejecución mediante docker run -e o env: / envFrom: de Kubernetes.
#!/usr/bin/env bash
# docker-entrypoint.sh
set -euo pipefail
# Validate required env vars
for var in DATABASE_URL REDIS_URL SECRET_KEY; do
: "${!var:?$var is required}"
done
export APP_PORT=${APP_PORT:-8000}
export WORKERS=${WORKERS:-$(nproc)}
echo "[entrypoint] Generating configuration..."
envsubst '${DATABASE_URL} ${REDIS_URL} ${SECRET_KEY} ${APP_PORT} ${WORKERS}' \
< /app/config/settings.toml.template \
> /app/config/settings.toml
echo "[entrypoint] Starting server on port ${APP_PORT} with ${WORKERS} workers"
exec gunicorn app:application \
--bind "0.0.0.0:${APP_PORT}" \
--workers "${WORKERS}"Patrón de Kubernetes con ConfigMap y envsubst
En Kubernetes, las variables de entorno se inyectan mediante env: o envFrom: en la especificación del Pod. El punto de entrada del contenedor llama a envsubst para materializar las configuraciones antes de iniciar el proceso; no se necesita un ConfigMap por entorno.
Así, los valores específicos de cada entorno se mantienen en Secrets y ConfigMaps de Kubernetes (para datos no confidenciales), mientras que la plantilla de configuración permanece en la imagen. Una imagen, muchos entornos.
- Compilación:
COPY nginx.conf.template /etc/nginx/templates/ - Ejecución: el punto de entrada ejecuta
envsubsty escribe/etc/nginx/nginx.conf - K8s inyecta:
APP_PORT,BACKEND_HOSTdesde un Secret/ConfigMap
Depurar envsubst: encontrar variables ausentes o sin resolver
Cuando una configuración generada contiene literalmente ${VAR} en lugar de un valor, la variable no se exportó o no se incluyó en la lista de sustitución. Use estas técnicas para depurar:
printenv | sort: muestra todas las variables exportadas- Compare los marcadores de posición de la plantilla con las variables exportadas mediante
grep - Ejecute
envsubsty busque en la salida patrones${que hayan quedado - Use
set -uen el script que realiza la llamada para que las referencias a variables no definidas en el código de Bash terminen inmediatamente la ejecución
#!/usr/bin/env bash
set -euo pipefail
TEMPLATE=/tmp/app.conf.template
OUTPUT=/tmp/app.conf
# Write a demo template
cat > "$TEMPLATE" << 'EOF'
host=${DB_HOST}
port=${DB_PORT}
name=${DB_NAME}
EOF
export DB_HOST=db.internal
export DB_PORT=5432
# DB_NAME intentionally left unset
envsubst < "$TEMPLATE" > "$OUTPUT"
# Detect unresolved placeholders
if grep -qE '\$\{[A-Z_]+\}' "$OUTPUT"; then
echo "ERROR: unresolved placeholders found:"
grep -oE '\$\{[A-Z_]+\}' "$OUTPUT" | sort -u
exit 1
fi
echo "Config OK:"
cat "$OUTPUT"Comprobación de conocimientos: sustitución selectiva con envsubst
Considere una plantilla de configuración de NGINX que contiene tanto la variable de su aplicación ${APP_PORT} como la variable nativa de NGINX $uri. Ejecute el siguiente comando:
envsubst < nginx.conf.template > nginx.conf
¿Cuál es el resultado?
Repaso de la lección: crear plantillas de configuración con envsubst y heredocs
Ahora dispone de un conjunto de herramientas listo para producción para generar configuraciones en tiempo de ejecución con Bash:
- envsubst reemplaza los marcadores
${VAR}de cualquier archivo de texto usando el entorno actual, sin scripting ni escapes especiales - La sustitución selectiva (
envsubst '${VAR1} ${VAR2}') protege las variables nativas de NGINX, Prometheus y herramientas similares frente a reemplazos accidentales - Los heredocs entrecomillados (
<< 'EOF') posponen la expansión del shell para que el contenido de la plantilla llegue intacto aenvsubst, sin necesidad de archivos temporales - Valide antes de sustituir: use
${VAR:?message}para terminar si faltan variables obligatorias y${VAR:-default}para las opcionales - Patrón de entrada de Docker: genere las configuraciones al iniciar el contenedor y después use
execpara el daemon, de modo que las señales se gestionen correctamente - Depure los marcadores sin resolver buscando en la salida patrones
${restantes antes de iniciar el proceso
Estos patrones mantienen inmutables las imágenes de sus contenedores, evitan que los secretos se almacenen en el control de versiones y garantizan la coherencia de las configuraciones en todos los entornos.
Preguntas frecuentes
¿La lección «Creación de plantillas de configuración con envsubst y heredocs» es gratis?
Sí — el texto completo de «Creación de plantillas de configuración con envsubst y heredocs» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Creación de plantillas de configuración con envsubst y heredocs»?
Genere la configuración en tiempo de ejecución a partir de variables de entorno mediante envsubst y heredocs entrecomillados. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Creación de plantillas de configuración con envsubst y heredocs»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Creación de Dockerfiles compactos y entrypoints de shell
- Creación de plantillas de configuración con envsubst y heredocs
- Scripting de recursos en la nube mediante CLI y jq
- Sondas de estado, puertas de disponibilidad y bucles de espera