Creare template di configurazione con envsubst e heredoc
Generi la configurazione a runtime dalle variabili d'ambiente usando envsubst e heredoc tra virgolette.
Creare template di configurazione con envsubst e heredoc è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.
Perché è importante generare la configurazione a runtime
Nei flussi di lavoro DevOps e con i container, i file di configurazione come nginx.conf, prometheus.yml e docker-compose.yml devono spesso cambiare tra gli ambienti — staging, produzione e DR. Inserire i valori direttamente nei file causa divergenze ed esposizione dei secret.
La soluzione è la generazione della configurazione a runtime: distribuisca un template con segnaposto, quindi inserisca i valori reali all'avvio tramite le variabili d'ambiente. In questo modo l'immagine rimane immutabile e la configurazione verificabile.
- Nessun secret incorporato nelle immagini
- Lo stesso artifact promosso tra gli ambienti
- Configurazione generata subito prima dell'avvio del processo
In Bash, due strumenti complementari rendono tutto questo semplicissimo: envsubst e gli heredoc quotati.
envsubst: il generatore di configurazione in una riga
envsubst è una piccola utility GNU che legge dallo stdin, sostituisce i segnaposto $VARIABLE e ${VARIABLE} con i rispettivi valori dell'ambiente corrente e scrive sullo stdout.
È incluso nel pacchetto gettext ed è disponibile praticamente in ogni distribuzione Linux e immagine base Docker.
- Funziona con qualsiasi formato testuale: NGINX, YAML, TOML, JSON, INI
- Non valuta la sintassi della shell: sostituisce solo i riferimenti alle variabili
- È sicuro: non esegue i comandi contenuti nel template
#!/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; }Sostituzione selettiva delle variabili
Per impostazione predefinita, envsubst sostituisce ogni $VAR che trova. Questo può alterare variabili NGINX come $uri o $host: sono direttive NGINX reali, non variabili d'ambiente.
Passi un elenco esplicito di variabili come primo argomento per limitare la sostituzione esclusivamente a quei nomi:
envsubst '$VAR1 $VAR2'L'argomento è una stringa racchiusa tra apici singoli (in modo che la shell non la espanda) contenente i nomi delle variabili da sostituire, separati da spazi o da ritorni a capo.
#!/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}'
File template su disco
Per le configurazioni reali, memorizzi il template in un file (ad es. nginx.conf.template) insieme al Dockerfile. All'avvio del container, esegua envsubst per produrre il file di configurazione finale prima di avviare il demone.
Questo è il pattern canonico utilizzato dall'immagine Docker ufficiale di 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.confHeredoc quotati: template inline senza file temporanei
Un heredoc quotato (che utilizza << 'EOF' con il delimitatore racchiuso tra virgolette singole) impedisce alla shell di espandere le variabili o eseguire sostituzioni di comandi all'interno del blocco. Il contenuto viene trattato come una stringa letterale.
Questo rende gli heredoc il modo ideale per scrivere un template inline e passarlo direttamente tramite pipe a envsubst, senza bisogno di un file intermedio.
<< EOF(senza virgolette) — la shell espande immediatamente$VAR<< 'EOF'(tra virgolette) — il contenuto è letterale; l'espansione viene rimandata aenvsubst
#!/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 shellCombinare gli heredoc con il reindirizzamento dell'output
Convogli un heredoc quotato tramite envsubst e reindirizzi il risultato a un file con una sola espressione. Questo è l'idioma più chiaro per generare file di configurazione in uno script dell'entrypoint.
Utilizzi la sostituzione selettiva ('${VAR1} ${VAR2}') quando il formato di destinazione (Prometheus, NGINX e così via) dispone di una propria sintassi $variable da proteggere.
#!/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}"Valori predefiniti e convalida prima della sostituzione
Non dia mai per scontato che tutte le variabili obbligatorie siano impostate. Utilizzi l'espansione dei parametri di Bash per fornire valori predefiniti o interrompere esplicitamente l'esecuzione:
${VAR:-default}— usadefaultseVARnon è impostata o è vuota${VAR:?error message}— interrompe l'esecuzione con un errore seVARnon è impostata o è vuota
Imposti questi valori prima di chiamare envsubst, in modo che il template riceva sempre un valore concreto oppure che lo script si interrompa subito mostrando un messaggio utile.
#!/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}"Generare configurazioni con più sezioni usando più heredoc
Per configurazioni complesse composte da sezioni logiche, è possibile generare ogni sezione indipendentemente e concatenarle, oppure utilizzare un unico heredoc che comprenda l'intero file. Entrambi gli approcci funzionano: scelga in base alla leggibilità.
Quando le sezioni vengono incluse condizionalmente (ad esempio, il blocco TLS solo se è impostato un percorso per il certificato), l'approccio con più heredoc e blocchi if risulta più chiaro.
#!/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"Pattern dell'entrypoint Docker
Il pattern consigliato per l'entrypoint Docker utilizza uno script shell (docker-entrypoint.sh) per generare le configurazioni all'avvio, quindi cedere il controllo al processo principale con exec. L'uso di exec sostituisce il processo della shell con il demone, così i segnali (SIGTERM, SIGINT) arrivano direttamente al demone: un aspetto fondamentale per un arresto ordinato.
I file template vengono aggiunti all'immagine durante la build; i valori vengono inseriti a runtime tramite docker run -e o Kubernetes env: / envFrom:.
#!/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}"Pattern Kubernetes ConfigMap + envsubst
In Kubernetes, le variabili d'ambiente vengono inserite tramite env: o envFrom: nella specifica del Pod. L'entrypoint del container chiama envsubst per generare le configurazioni prima dell'avvio del processo: non è necessario creare una ConfigMap per ogni ambiente.
In questo modo i valori specifici dell'ambiente restano in Kubernetes, nelle Secrets e nelle ConfigMaps (per i dati non sensibili), mentre il template di configurazione risiede nell'immagine. Una sola immagine per molti ambienti.
- Build:
COPY nginx.conf.template /etc/nginx/templates/ - Runtime: l'entrypoint esegue
envsubste scrive/etc/nginx/nginx.conf - K8s inserisce:
APP_PORT,BACKEND_HOSTda una Secret o ConfigMap
Debugging di envsubst: trovare variabili mancanti o non risolte
Quando una configurazione generata contiene il valore letterale ${VAR} invece di un valore effettivo, la variabile non è stata esportata oppure non è stata inclusa nell'elenco di sostituzione. Utilizzi queste tecniche per eseguire il debugging:
printenv | sort— elenca tutte le variabili esportate- Confronti i segnaposto del template con le variabili esportate usando
grep - Esegua
envsubste cerchi nell'output i pattern${ancora presenti - Utilizzi
set -unello script chiamante, così i riferimenti a variabili non impostate nel codice Bash interrompono immediatamente l'esecuzione
#!/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"Verifica delle conoscenze: sostituzione selettiva con envsubst
Consideri un template di configurazione NGINX che contiene sia la variabile dell'applicazione ${APP_PORT} sia la variabile nativa di NGINX $uri. Esegue il comando seguente:
envsubst < nginx.conf.template > nginx.conf
Qual è il risultato?
Riepilogo della lezione: creare template per le configurazioni con envsubst ed heredoc
Ora dispone di uno strumentario adatto alla produzione per generare configurazioni a runtime in Bash:
- envsubst sostituisce i segnaposto
${VAR}in qualsiasi file di testo usando l'ambiente corrente, senza dover scrivere script né gestire caratteri di escape speciali - La sostituzione selettiva (
envsubst '${VAR1} ${VAR2}') protegge le variabili native di NGINX, Prometheus e strumenti simili dalla sostituzione accidentale - Gli heredoc quotati (
<< 'EOF') rimandano l'espansione della shell, così il contenuto del template arriva intatto aenvsubst, senza bisogno di file temporanei - Convalidare prima della sostituzione: utilizzi
${VAR:?message}per interrompere l'esecuzione in caso di variabili obbligatorie mancanti e${VAR:-default}per quelle facoltative - Pattern dell'entrypoint Docker: generi le configurazioni all'avvio del container, quindi utilizzi
execper il demone, così i segnali vengono gestiti correttamente - Eseguire il debugging dei segnaposto non risolti cercando nell'output i pattern
${ancora presenti prima dell'avvio del processo
Questi pattern mantengono immutabili le immagini dei container, tengono i segreti fuori dal controllo versione e garantiscono configurazioni coerenti in ogni ambiente.
Domande Frequenti
La lezione «Creare template di configurazione con envsubst e heredoc» è gratuita?
Sì — il testo completo di «Creare template di configurazione con envsubst e heredoc» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.
Cosa imparerò in «Creare template di configurazione con envsubst e heredoc»?
Generi la configurazione a runtime dalle variabili d'ambiente usando envsubst e heredoc tra virgolette. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare DevOps Bootcamp?
Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Creare template di configurazione con envsubst e heredoc»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?
Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Scrivere Dockerfile essenziali ed entrypoint shell
- Creare template di configurazione con envsubst e heredoc
- Gestire risorse cloud tramite CLI e jq
- Probe di salute, controlli di readiness e cicli di attesa