Variables de entorno y configuración Docker | Nicolás Garzón
Texto
Copiar misma image por digest
+ configuración de desarrollo
→ entorno de desarrollo
misma image por digest
+ configuración de producción
→ entorno de producciónReconstruir una imagen por ambiente rompe trazabilidad: deja de ser posible demostrar que producción ejecuta exactamente el artefacto validado en staging.
puerto interno;
nivel de logs;
feature flags no sensibles;
URLs públicas;
timeouts;
límites funcionales;
nombres de colas o topics.
passwords;
API keys;
private keys;
tokens de acceso;
credenciales de database.
archivos YAML o JSON;
certificados y cadenas de confianza;
reglas extensas;
listas de endpoints;
políticas.
El canal debe elegirse según sensibilidad, tamaño, rotación y forma de consumo.
Existe durante build según alcance:
docker
Copiar ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-slimNo queda disponible automáticamente como variable de runtime, pero puede aparecer en logs, historial o outputs. No es un secret store.
Persiste en metadata y runtime:
docker
Copiar ENV NODE_ENV=production \
PORT=3000Es apropiado para defaults no sensibles ligados a la imagen. Puede sobrescribirse al crear el contenedor.
Aspecto ARG ENV Build Sí Sí desde su declaración Runtime predeterminado No Sí Secretos No No recomendado Afecta resultado/cache Puede Puede
Bash
Copiar docker run \
--env NODE_ENV = production \
--env PORT = 3000 \
my-apiTambién puede heredarse un valor del shell:
Bash
Copiar export LOG_LEVEL = info
docker run --env LOG_LEVEL my-apiEste patrón puede producir valores ausentes o diferentes entre terminales. Para despliegues, prefiere configuración explícita y validada.
Bash
Copiar docker run --env-file /etc/my-api/runtime.env my-apiTexto
Copiar NODE_ENV=production
PORT=3000
LOG_LEVEL=info
REQUEST_TIMEOUT_MS=10000
no se copia automáticamente a la image;
simplifica varios valores;
sigue siendo un archivo sensible si contiene secretos;
necesita permisos, distribución y rotación;
no debe versionarse con credenciales reales.
Mantén un .env.example con nombres y valores seguros de muestra, no secretos.
La configuración puede venir de:
defaults del código;
ENV de la image;
env files;
valores de Compose;
flags de CLI;
archivos montados;
servicios externos.
Una variable definida en varios niveles crea ambigüedad. Documenta una política única y utiliza:
Bash
Copiar docker inspect container --format '{{json .Config.Env}}' para revisar el resultado efectivo. Evita imprimir valores sensibles en soporte o tickets.
La aplicación debe validar configuración antes de aceptar tráfico.
Ejemplo TypeScript con Zod:
TypeScript
Copiar import { z } from 'zod' ;
const environmentSchema = z. object ( {
NODE_ENV : z. enum ( [ 'development' , 'test' , 'production' ] ) ,
PORT : z. coerce. number ( ) . int ( ) . min ( 1 ) . max ( 65535 ) ,
DATABASE_URL : z. string ( ) . url ( ) ,
REQUEST_TIMEOUT_MS : z. coerce. number ( ) . int ( ) . positive ( ) . default ( 10_000 ) ,
} ) ;
export const environment = environmentSchema. parse ( process. env) ;
falla rápido;
evita errores tardíos;
documenta tipos;
elimina conversiones dispersas;
distingue ausencia de valor de valor inválido.
Los mensajes de error no deben mostrar passwords o URLs completas con credenciales.
Todas las variables de entorno llegan como strings.
JavaScript
Copiar if ( process. env. FEATURE_ENABLED ) {
} Convierte explícitamente y define formatos aceptados.
Unidades también deben formar parte del nombre o esquema:
Texto
Copiar REQUEST_TIMEOUT_MS=10000
CACHE_TTL_SECONDS=300Evita TIMEOUT=10 sin unidad.
Estos estados no son equivalentes:
Texto
Copiar VARIABLE no definida
VARIABLE=
VARIABLE=""La aplicación debe decidir si vacío significa borrar, usar default o error. Herramientas de Compose y shells pueden interpolar valores de manera diferente.
Environment es conveniente, pero puede aparecer en:
docker inspect;
dumps de procesos;
sistemas de soporte;
logs accidentales;
interfaces de orquestación;
crash reports.
Para secretos de alto impacto, prefiere mounts de archivos o integración con un secret manager.
Ejemplo de consumo por archivo:
Texto
Copiar /run/secrets/db_passwordLa aplicación puede soportar convención _FILE:
Texto
Copiar DB_PASSWORD_FILE=/run/secrets/db_passwordEl archivo también necesita permisos y rotación. No existe un canal sin responsabilidades.
Un archivo montado es útil para contenido estructurado:
Bash
Copiar docker run \
--mount type = bind,src= /etc/my-api/config.yaml,dst= /app/config.yaml,readonly \
my-api
estructura y jerarquía;
validación con schema;
lectura atomizada;
permisos de filesystem.
path acoplado al host;
ownership;
actualizaciones parciales;
aplicación que no recarga;
archivo ausente en otro nodo.
La configuración debe administrarse y distribuirse, no editarse dentro del container.
Los flags de runtime permiten activar comportamiento sin construir otra image. Pero deben tener:
owner;
fecha de eliminación;
valor predeterminado seguro;
observabilidad;
estrategia de rollback;
control de acceso.
Un flag no debe esconder durante años dos arquitecturas completas dentro del código.
Algunas aplicaciones consultan un servicio central. Esto permite cambios sin recrear, pero introduce:
dependencia de disponibilidad;
cache local;
consistencia eventual;
autenticación;
auditoría;
comportamiento ante pérdida de conexión.
Define qué valores pueden cambiar en caliente y cuáles requieren restart. No todas las librerías releen process.env; normalmente el environment se fija al crear el proceso.
Rotar una credencial puede requerir:
crear el nuevo valor;
permitir temporalmente ambos valores;
actualizar mounts o configuración;
recrear o recargar servicios;
verificar conexiones nuevas;
retirar el valor anterior;
auditar fallos.
Cambiar un archivo en host no garantiza que la aplicación lo relea. Pools existentes pueden conservar conexiones autenticadas con la credencial anterior.
No registres todo process.env al iniciar. Puede incluir:
tokens inyectados por plataforma;
credenciales del sistema;
URLs con passwords;
secretos no conocidos por el desarrollador.
Registra únicamente nombres permitidos y valores no sensibles:
JavaScript
Copiar console. info ( {
nodeEnv : environment. NODE_ENV ,
port : environment. PORT ,
requestTimeoutMs : environment. REQUEST_TIMEOUT_MS ,
} ) ; Variables usadas durante un build de frontend pueden quedar incorporadas al bundle público. Un valor llamado API_SECRET no se vuelve privado por estar en .env.
build-time public config;
runtime server config;
secretos de backend.
En Next.js y otros frameworks, los prefijos públicos suelen indicar que el valor llegará al navegador. Trátalo como público.
Texto
Copiar registry.example.com/api@sha256:ATexto
Copiar LOG_LEVEL=debug
DATABASE_URL=postgres://db-dev/app
FEATURE_NEW_CHECKOUT=trueTexto
Copiar LOG_LEVEL=info
DATABASE_URL=postgres://db-staging/app
FEATURE_NEW_CHECKOUT=trueTexto
Copiar LOG_LEVEL=warn
DATABASE_URL_FILE=/run/secrets/database_url
FEATURE_NEW_CHECKOUT=falseEl digest es el mismo. Cambian dependencias y configuración externas.
La precedencia puede producir un valor inesperado. Inspecciona configuración resuelta.
El parser del env file y quoting importan. Prueba el formato real; no copies sintaxis de shell sin verificar.
Un secreto o archivo puede incluir \r y causar autenticación inválida.
Leer un archivo mediante cat o editor puede conservar newline. La aplicación debe conocer el formato, no aplicar trim() indiscriminadamente a certificados o datos binarios.
Las variables se fijaron al crear la instancia. Debes recrear o usar un canal dinámico.
Si falta AUTH_ENABLED, la app desactiva auth. Los defaults de seguridad deben fallar cerrados.
Consecuencia: secreto en layers y artefacto distinto por ambiente.
Corrección: runtime config y secret mounts.
Consecuencia: "false" se interpreta como true o un timeout inválido aparece tarde.
Corrección: schema al inicio.
Consecuencia: staging y producción ejecutan artefactos distintos.
Corrección: build once, configure at runtime.
Consecuencia: filtración de credenciales.
Corrección: allowlist de valores no sensibles.
Consecuencia: precedencia impredecible.
Corrección: una fuente por categoría y documentación clara.
Construye una vez y configura al ejecutar.
ARG y ENV cumplen funciones distintas, pero ninguno es un vault.
Todas las variables llegan como strings y deben validarse.
Secrets en environment pueden quedar expuestos por herramientas operativas.
Configuración dinámica introduce una nueva dependencia que debe diseñarse.
Rotar un valor requiere verificar que la aplicación realmente lo consume.
Comprueba lo aprendido
¿Por qué dos imágenes construidas desde el mismo commit pueden diferir entre ambientes?
Diseña un schema de configuración para una API con timeouts y secrets.
¿Qué valores usarías como environment y cuáles como archivos montados?
¿Qué riesgos tiene registrar process.env?
Describe una rotación de password sin downtime.
Puertos, EXPOSE y publicación , donde se diferencia el puerto del proceso, la red del container y la exposición en el host.