Variables de Entorno y Argumentos en Build

Lectura
15 min~7 min lectura
CONCEPTO CLAVE: En Docker, los ARG son variables disponibles únicamente durante el proceso de construcción de la imagen, mientras que ENV son variables persistentes disponibles tanto en build como en runtime. Comprender esta diferencia es fundamental para crear Dockerfiles eficientes y seguros.

¿Por qué importan las variables?

En el desarrollo moderno, las aplicaciones necesitan adaptarse a diferentes entornos, configuraciones y contextos. Docker proporciona dos mecanismos principales para manejar configuraciones: los argumentos de construcción (ARG) y las variables de entorno (ENV). Aunque a simple vista pueden parecer similares, tienen propósitos y comportamientos muy distintos que exploraremos en detalle.

ARG: Argumentos de Construcción

Los argumentos ARG son instrucciones del Dockerfile que permiten pasar valores durante el proceso de docker build. Su alcance está limitado exclusivamente a la construcción de la imagen, lo que significa que no persisten en la imagen final ni están disponibles cuando el contenedor se ejecuta.

Sintaxis básica de ARG

ARG NOMBRE_VARIABLE
ARG NOMBRE_VARIABLE=valor_por_defecto

Ejemplo práctico

# Dockerfile
ARG NODE_VERSION
ARG NPM_TOKEN

RUN echo "Installing dependencies..."
RUN npm install --registry=https://registry.npmjs.org/ --token=$NPM_TOKEN

FROM node:${NODE_VERSION:-18}

Para construir con estos argumentos:

docker build --build-arg NODE_VERSION=20 --build-arg NPM_TOKEN=mi_token_secreto -t mi-app:latest .
📌 Los argumentos ARG son ideales para valores que cambian entre builds pero no necesitan estar disponibles en runtime, como versiones de dependencias, tokens de construcción o flags de compilación.

ENV: Variables de Entorno

Las variables ENV persisten tanto durante el build como en el contenedor ejecutándose. Son el mecanismo principal para configurar el comportamiento de la aplicación en runtime y pueden ser sobrescritas al iniciar el contenedor.

Sintaxis básica de ENV

ENV NOMBRE_VARIABLE=valor
ENV NOMBRE_VARIABLE=valor

Ejemplo práctico

# Dockerfile
FROM node:18-alpine

ENV NODE_ENV=production
ENV APP_PORT=3000
ENV DATABASE_HOST=localhost

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
EXPOSE ${APP_PORT}

CMD ["node", "server.js"]
💡 Puedes sobrescribir variables ENV al ejecutar el contenedor: docker run -e APP_PORT=8080 -e DATABASE_HOST=db-server mi-app

Comparación directa: ARG vs ENV

Característica ARG ENV
Disponibilidad en build ✅ Sí ✅ Sí
Disponibilidad en runtime ❌ No ✅ Sí
Persiste en imagen ❌ No ✅ Sí
Sobrescribible en run ❌ No aplica ✅ Sí (-e flag)
Uso típico Versiones, tokens de build Configuración de aplicación

Uso combinado: El patrón ARG + ENV

Una práctica muy común es combinar ambos mecanismos. Usamos ARG para recibir valores durante el build y luego los asignamos a ENV para que estén disponibles en runtime:

# Dockerfile multi-stage
ARG VERSION=1.0.0
ARG BUILD_DATE

# Stage de construcción
FROM node:18 AS builder
ARG VERSION
ARG BUILD_DATE
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage de producción
FROM node:18-alpine
ARG VERSION
ARG BUILD_DATE

# Convertimos ARG en ENV para runtime
ENV APP_VERSION=${VERSION}
ENV BUILD_DATE=${BUILD_DATE}
ENV NODE_ENV=production

WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

EXPOSE 3000
CMD ["node", "dist/index.js"]
📌 Este patrón es especialmente útil porque permite pasar información de build (como la versión) que deseas que esté disponible en la aplicación final sin exponerla en el comando de ejecución.

Variables predefinidas de Docker

Docker proporciona varios ARG predefinidos que puedes usar sin declararlos:

Variable Descripción
HTTP_PROXY Proxy HTTP configurado en el daemon
HTTPS_PROXY Proxy HTTPS configurado en el daemon
FTP_PROXY Proxy FTP configurado en el daemon
NO_PROXY Hosts excluidos del proxy
⚠️ No necesitas declarar estas variables predefinidas, pero puedes sobrescribirlas si es necesario. Además, estas también se convierten en variables ENV automáticamente.

Scope de ARG: Limitando su alcance

Los ARG tienen un concepto de scope o alcance. Un ARG definido antes del primer FROM en un Dockerfile con múltiples stages solo está disponible en stages posteriores si se redefine:

# ARG global disponible desde el inicio
ARG BASE_VERSION=1.0

FROM node:18 AS builder
# BASE_VERSION está disponible aquí

FROM python:3.11 AS api
# BASE_VERSION NO está disponible aquí automáticamente
ARG BASE_VERSION
# Ahora BASE_VERSION está disponible
💡 Para hacer un ARG disponible en todos los stages, defínelo antes del primer FROM en un Dockerfile multi-stage.

Ejemplo completo: Aplicación Node.js con configuración

  1. Crear la estructura del proyecto:
mi-proyecto/
├── Dockerfile
├── package.json
├── src/
│   └── index.js
└── .dockerignore
  1. Definir el Dockerfile completo:
# ============================================
# ARG para construcción
# ============================================
ARG NODE_VERSION=18
ARG NPM_REGISTRY=https://registry.npmjs.org/

# ============================================
# Imagen base con versión configurable
# ============================================
FROM node:${NODE_VERSION}-alpine AS builder

# Argumentos de build del stage
ARG NPM_REGISTRY
ARG BUILD_ENV=production

# Configurar npm registry
RUN npm config set registry ${NPM_REGISTRY}

WORKDIR /app

# Copiar archivos de dependencias primero (optimización de caché)
COPY package*.json ./

# Instalar dependencias
RUN npm ci --only=${BUILD_ENV}

# Copiar código fuente
COPY src/ ./src/

# ============================================
# Imagen de producción
# ============================================
FROM node:${NODE_VERSION}-alpine AS production

# ============================================
# ENV para runtime (algunos desde ARG)
# ============================================
ARG NODE_VERSION
ARG BUILD_ENV

ENV NODE_ENV=${BUILD_ENV}
ENV APP_ENV=production
ENV LOG_LEVEL=info
ENV API_TIMEOUT=5000
ENV MAX_CONNECTIONS=100

WORKDIR /app

# Copiar solo lo necesario del stage de build
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/src ./src

# Crear usuario no-root para seguridad
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodeuser -u 1001

USER nodeuser

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "src/index.js"]
  1. Construir la imagen:
# Construcción default
docker build -t mi-app:default .

# Construcción con argumentos personalizados
docker build \
  --build-arg NODE_VERSION=20 \
  --build-arg BUILD_ENV=staging \
  --build-arg NPM_REGISTRY=https://registry.npmmirror.com \
  -t mi-app:staging \
  .
  1. Ejecutar con sobrescritura de ENV:
# Ejecutar en producción
docker run -d \
  -p 3000:3000 \
  -e LOG_LEVEL=debug \
  -e MAX_CONNECTIONS=200 \
  --name mi-app-prod \
  mi-app:default

# Ejecutar en desarrollo
docker run -d \
  -p 3000:3000 \
  -e NODE_ENV=development \
  -e LOG_LEVEL=debug \
  --name mi-app-dev \
  mi-app:default
📌 Este enfoque permite usar la misma imagen para múltiples entornos, cambiando solo las variables de entorno en tiempo de ejecución.

Mejores prácticas

💡 Utiliza valores por defecto: Siempre que sea posible, proporciona valores por defecto sensatos para tus ARG y ENV. Esto facilita el uso de la imagen sin configuración adicional.
⚠️ No expongas secretos en ENV: Las variables ENV persisten en la imagen y pueden ser leídas con docker history o inspeccionando la configuración del contenedor. Para secretos, usa mecanismos como Docker Secrets o montajes de volúmenes.
💡 Orden de caché: Coloca las instrucciones que usan ARG que cambian frecuentemente al final del Dockerfile, después de las operaciones que quieres mantener en caché (como la instalación de dependencias).
Ver más: Ejemplo de manejo de secretos

Para manejar secretos de forma segura, evita ponerlos directamente en ENV:

# INCORRECTO - Expone el secreto
ENV API_KEY=mi_clave_secreta

# CORRECTO - Usar montajes de volumen o Docker Secrets
# Al ejecutar el contenedor:
docker run -v /ruta/local/secrets.json:/secrets/api.json:ro \
  -e API_KEY=$(cat /ruta/local/api_key.txt) \
  mi-app

En Swarm, usa Docker Secrets para gestión segura de credenciales sensibles.

Inspección de variables

Para verificar qué variables ENV están configuradas en una imagen:

# Ver todas las variables ENV
docker image inspect mi-app:latest --format='{{range .Config.Env}}{{println .}}{{end}}'

# Ver solo las variables personalizadas
docker image inspect mi-app:latest --format='{{range .Config.Env}}{{if not (hasPrefix . "PATH=")}}{{println .}}{{end}}{{end}}'

# Ver la configuración completa
docker inspect mi-app:latest | grep -A 50 '"Env"'
Las variables de entorno son la forma más flexible de configurar contenedores. Aprende a combinarlas con ARG para builds reproducibles y despliegues versátiles.

Resumen práctico

Recuerda esta regla simple:

  • ¿Necesitas el valor SOLO durante el build? → Usa ARG
  • ¿Necesitas el valor en runtime? → Usa ENV
  • ¿Necesitas el valor en ambos? → Combina ambos: ARGENV
🧠 Quiz

¿Cuál es la diferencia principal entre ARG y ENV en Docker?

  • A) No hay diferencia, son intercambiables
  • B) ARG solo existe durante el build, ENV persiste en runtime
  • C) ENV solo existe durante el build, ARG persiste en runtime
  • D) ARG es más seguro que ENV
✅ Respuesta correcta: B) Los argumentos ARG están disponibles únicamente durante la construcción de la imagen, mientras que las variables ENV persisten tanto en el build como cuando el contenedor se ejecuta.
🧠 Quiz

¿Cuándo deberías usar el patrón ARG + ENV combinado?

  • A) Nunca, es una mala práctica
  • B) Cuando necesitas un valor tanto en build como en runtime
  • C) Solo cuando trabajas con múltiples stages
  • D) Únicamente para variables de configuración
✅ Respuesta correcta: B) El patrón combinado es ideal cuando necesitas recibir un valor durante el build (con ARG) y luego disponibilizarlo en runtime (convirtiéndolo en ENV).