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.
ARGvive durante el build de la imagen- Mientras que
ENVqueda 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:
- Build time: cuando Docker construye la imagen con
docker build. - Runtime: cuando Docker arranca un contenedor con
docker runodocker 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ón | Momento | Queda en la imagen | Disponible en el contenedor |
|---|---|---|---|
ARG | Build | No como variable final | No, salvo que la copies a ENV |
ENV | Build y runtime | Sí | Sí |
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
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)
Esto es útil para valores que forman parte del comportamiento normal de la imagen:
NODE_ENV=productionASPNETCORE_URLS=http://+:8080TZ=Europe/Madrid- 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"]
Y lo construimos así:
docker build \
--build-arg NODE_VERSION=20 \
--build-arg BUILD_MODE=production \
-t mi-app .
Aquí pasan varias cosas:
NODE_VERSIONdecide qué imagen base usamos.BUILD_MODEexiste durante el build.NODE_ENVqueda guardado en la imagen como variable de entorno.PORTqueda 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
Y esto también:
docker build --build-arg API_TOKEN=supersecreto .
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.
