dockerfile-arg-env

ARG y ENV en Dockerfile

  • 5 min

ARG y ENV en Dockerfile son dos formas distintas de definir variables dentro del proceso de construcción.

Parecen casi lo mismo, porque ambas usan nombres y valores. Pero no lo son.

  • ARG vive durante el build de la imagen
  • Mientras que ENV queda guardado en la imagen final y estará disponible cuando arranque el contenedor.

Esta diferencia es importante. Porque si mezclamos los conceptos, acabamos con Dockerfiles raros, imágenes difíciles de configurar o, peor todavía, secretos y contraseñas metidos dentro de la imagen.

Variables de build y variables de runtime

Lo primero es separar dos momentos distintos:

  1. Build time: cuando Docker construye la imagen con docker build.
  2. Runtime: cuando Docker arranca un contenedor con docker run o docker compose up.

ARG pertenece principalmente al primer momento. Sirve para parametrizar la construcción.

ENV pertenece al segundo momento. Sirve para definir variables disponibles dentro del contenedor.

Podemos resumirlo así:

InstrucciónMomentoQueda en la imagenDisponible en el contenedor
ARGBuildNo como variable finalNo, salvo que la copies a ENV
ENVBuild y runtime

Vamos a verlo despacio 👇.

ENV

La instrucción ENV define una variable de entorno dentro de la imagen. Por ejemplo:

ENV NODE_ENV=production
ENV APP_PORT=3000
Copied!

Cuando un contenedor arranque desde esa imagen, tendrá esas variables disponibles.

En una aplicación Node.js, por ejemplo, podríamos leer:

console.log(process.env.NODE_ENV)
console.log(process.env.APP_PORT)
Copied!

Esto es útil para valores que forman parte del comportamiento normal de la imagen:

  1. NODE_ENV=production
  2. ASPNETCORE_URLS=http://+:8080
  3. TZ=Europe/Madrid
  4. rutas como PATH

Es decir, cosas que tienen sentido como valores por defecto de la imagen.

Ejemplo completo

Vamos a juntar las dos cosas en un Dockerfile sencillo:

ARG NODE_VERSION=22

FROM node:${NODE_VERSION}-alpine

WORKDIR /app

ARG BUILD_MODE=production
ENV NODE_ENV=${BUILD_MODE}
ENV PORT=3000

COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 3000
CMD ["npm", "start"]
Copied!

Y lo construimos así:

docker build \
  --build-arg NODE_VERSION=20 \
  --build-arg BUILD_MODE=production \
  -t mi-app .
Copied!

Aquí pasan varias cosas:

  1. NODE_VERSION decide qué imagen base usamos.
  2. BUILD_MODE existe durante el build.
  3. NODE_ENV queda guardado en la imagen como variable de entorno.
  4. PORT queda como valor por defecto para el contenedor.

Es un patrón bastante habitual.

No uséis ARG ni ENV para secretos

Ni ARG ni ENV son un buen sitio para contraseñas, tokens o claves privadas.

Con ENV, el problema es evidente: el valor queda dentro de la imagen y puede verse con herramientas como docker inspect.

Con ARG, la cosa es un poco más traicionera. Aunque no quede como variable final del contenedor, puede aparecer en el historial de la imagen o en metadatos del build.

Por tanto, esto está mal:

ARG API_TOKEN=supersecreto
ENV DB_PASSWORD=otraclave
Copied!

Y esto también:

docker build --build-arg API_TOKEN=supersecreto .
Copied!

Si necesitáis secretos durante el build, mirad mecanismos como BuildKit secrets. Si necesitáis secretos al arrancar el contenedor, pasadlos desde el entorno, Docker Compose, un gestor de secretos o la plataforma donde despleguéis.