¿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 .
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"]
docker run -e APP_PORT=8080 -e DATABASE_HOST=db-server mi-appComparació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"]
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 |
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
FROM en un Dockerfile multi-stage.Ejemplo completo: Aplicación Node.js con configuración
- Crear la estructura del proyecto:
mi-proyecto/
├── Dockerfile
├── package.json
├── src/
│ └── index.js
└── .dockerignore
- 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"]
- 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 \
.
- 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
Mejores prácticas
docker history o inspeccionando la configuración del contenedor. Para secretos, usa mecanismos como Docker Secrets o montajes de volúmenes.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:
ARG→ENV
¿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
¿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