DevOpsLesson
DevOpsLesson

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Stay Updated

Get notified about new tutorials and features.

Tutorials

  • What is DevOps?
  • Docker Tutorial
  • Terraform Tutorial
  • CI/CD Pipeline
  • All Tutorials

Roadmaps

  • DevOps Engineer
  • Cloud Engineer
  • SRE Path
  • All Roadmaps

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 DevOpsLesson. All rights reserved.

DOCKERKUBERNETESTERRAFORMAWSCI/CDLINUXGITDEVOPS ROADMAPCLOUD ROADMAPSRE ROADMAPGIT CHEATSHEETDOCKER CHEATSHEETK8S CHEATSHEETTF CHEATSHEETLINUX CHEATSHEETDOCKERFILE LINTERYAML VALIDATORCRON PARSERREGEX TESTER

Docker Tutorial

Introduction to docker
Why Use Docker?
Docker vs Virtual Machines
Installing Docker
Key Docker Concepts
Docker Images
Docker Containers
Writing Dockerfiles
Docker Volumes

ENV & ARG

PreviousPrev
Next

Inject variables into your builds and containers. Learn the critical difference between ENV (runtime) and ARG (build-time only), and how to use both safely.

ENV & ARG

Both ENV and ARG define variables in a Dockerfile, but they live in completely different phases. Confusing them leads to either variables that don't exist at runtime or secrets being baked into the image. The distinction is straightforward once you see it clearly.

The Core Difference

ARGENV
Available duringBuild onlyBuild + Runtime
Visible in running containerNoYes
Visible in docker inspectNoYes
Override with--build-arg-e at docker run
Persists in image layersNoYes
Safe for secretsNo (still visible in history)No

ENV - Runtime Environment Variables

ENV sets variables that are available both during the build (in subsequent RUN instructions) and inside every container created from the image.

ENV NODE_ENV=production
ENV PORT=3000
ENV LOG_LEVEL=info

# Multiple in one instruction (preferred)
ENV NODE_ENV=production \
    PORT=3000 \
    LOG_LEVEL=info

ENV values are baked into the image and visible in docker inspect. Override them at runtime with -e:

# Override ENV defaults at run time
docker run -e LOG_LEVEL=debug -e PORT=4000 myapp

Good uses for ENV:

  • Default configuration values (PORT, LOG_LEVEL, NODE_ENV)
  • Path variables (PATH, PYTHONPATH, GOPATH)
  • Tool configuration (PIP_NO_CACHE_DIR=1, NPM_CONFIG_LOGLEVEL=warn)

Never use ENV for secrets - they're stored in the image and visible via docker inspect.

ARG - Build-Time Variables

ARG variables only exist during docker build. They're gone when the container runs.

ARG APP_VERSION=1.0.0
ARG BUILD_DATE
ARG GIT_COMMIT

RUN echo "Building version $APP_VERSION on $BUILD_DATE ($GIT_COMMIT)"

Pass them at build time with --build-arg:

docker build \
  --build-arg APP_VERSION=2.1.0 \
  --build-arg BUILD_DATE=$(date -u +%Y-%m-%d) \
  --build-arg GIT_COMMIT=$(git rev-parse --short HEAD) \
  -t myapp:2.1.0 .

If no --build-arg is passed and no default is set, the variable is empty.

Using ARG Before FROM

ARG is the only instruction that can appear before FROM. Use it to parameterize the base image version:

ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine

# This ARG is scoped before FROM - need to re-declare to use after FROM
ARG NODE_VERSION
RUN echo "Running on Node $NODE_VERSION"

Note: An ARG before FROM is in a different scope. To use it after FROM, declare it again (with or without a default).

This pattern lets you build the same Dockerfile with different base versions:

docker build --build-arg NODE_VERSION=18 -t myapp:node18 .
docker build --build-arg NODE_VERSION=20 -t myapp:node20 .

Combining ARG and ENV

A common pattern - use ARG to accept a build-time value, then promote it to ENV so it's available at runtime:

ARG APP_VERSION=1.0.0
ENV APP_VERSION=${APP_VERSION}

ARG PORT=3000
ENV PORT=${PORT}
# Build with a specific version, available at runtime
docker build --build-arg APP_VERSION=2.5.0 -t myapp:2.5.0 .
docker run myapp:2.5.0
# Inside container: echo $APP_VERSION → 2.5.0
Exercise

Parameterize a Build

You want to build an image tagged myapp:3.0.0, passing a build argument APP_VERSION=3.0.0 (which the Dockerfile promotes from ARG to ENV). Then you want to run the container and print the value of APP_VERSION from inside it. Which sequence of commands is correct?

ARG and the Build Cache

ARG values affect layer caching. Changing an --build-arg value invalidates the cache from the point the ARG is first used:

ARG CACHE_BUST=1
RUN apt-get update && apt-get upgrade -y   # Reruns when CACHE_BUST changes
# Force a fresh apt-get update by changing the cache busting value
docker build --build-arg CACHE_BUST=$(date +%s) -t myapp .

This is a controlled way to invalidate specific layers without rebuilding everything.

Secrets in Builds (Don't Use ARG or ENV)

A common mistake - passing secrets as ARG or ENV:

# Secret visible in docker history even after the layer is "deleted"
ARG NPM_TOKEN
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc && \
    npm install && \
    rm .npmrc

The secret is embedded in the layer even after .npmrc is deleted. Use BuildKit's --secret mount instead:

#  Secret is never stored in any layer
RUN --mount=type=secret,id=npm_token \
    echo "//registry.npmjs.org/:_authToken=$(cat /run/secrets/npm_token)" > .npmrc && \
    npm install && \
    rm .npmrc
docker build --secret id=npm_token,env=NPM_TOKEN -t myapp .

Predefined ARGs

Docker provides several pre-defined build arguments you can use without declaring them:

# Available without ARG declaration
RUN echo $HTTP_PROXY
RUN echo $HTTPS_PROXY
RUN echo $NO_PROXY
RUN echo $TARGETPLATFORM   # e.g. linux/amd64
RUN echo $TARGETOS         # e.g. linux
RUN echo $TARGETARCH       # e.g. amd64

These are automatically passed from the build environment or docker build --build-arg.

Summary

  • ENV is for runtime configuration - visible in containers and docker inspect
  • ARG is for build-time parameters - gone after the build completes
  • Use ARG before FROM to parameterize the base image version
  • Promote ARG to ENV when you need a build argument to also be a runtime variable
  • Never pass secrets via ARG or ENV - use BuildKit's --secret mount flag instead
  • Changing an --build-arg value invalidates the cache from where it's first used
PreviousPrev
Next

Continue Learning

COPY & ADD

Copy files into your image correctly. Learn the difference between COPY and ADD, how to set ownership, use .dockerignore, and avoid common mistakes.

7 min·Easy

RUN

Execute commands at build time. Learn shell vs exec form, how to keep layers lean, combine commands efficiently, and avoid bloated images.

8 min·Easy

CMD & ENTRYPOINT

Define how your container starts. Understand exec vs shell form, the difference between CMD and ENTRYPOINT, and how to combine them for flexible, production-ready containers.

9 min·Easy

Explore Related Topics

Ku

Kubernetes Tutorials

Orchestrate your Docker containers at scale

CI

CI/CD Tutorials

Automate Docker builds and deployments in pipelines

Try the Tool

Dockerfile Linter

Instantly lint your Dockerfile for best-practice violations and security issues.

On This Page

ENV & ARGThe Core Difference`ENV` - Runtime Environment Variables`ARG` - Build-Time VariablesUsing ARG Before FROMCombining ARG and ENVARG and the Build CacheSecrets in Builds (Don't Use ARG or ENV)Predefined ARGsSummary