Skip to content

Lesson 2.3: Environment Variables and Arguments

Welcome to Lesson 2.3! Building on your Dockerfile knowledge, we now explore how to make your images more flexible and configurable. Environment variables and build arguments are essential tools for parameterizing your builds and containers. They allow you to change behavior without modifying the Dockerfile, making your images reusable across different environments.


Learning Objectives

TIP

By the end of this lesson, you will be able to:

  • Differentiate between ENV and ARG instructions.
  • Set and use environment variables in a Dockerfile and at runtime.
  • Pass build-time variables with --build-arg.
  • Understand the scope and persistence of variables.
  • Apply best practices for using variables securely.

1. Environment Variables (ENV)

The ENV instruction sets an environment variable that persists in the image and is available to all subsequent instructions and to running containers.

Syntax:

dockerfile
ENV <key>=<value> ...
  • You can set multiple variables in one line: ENV key1=value1 key2=value2.
  • The variable is available in any RUN, CMD, ENTRYPOINT instructions that follow.

Example:

dockerfile
FROM alpine
ENV APP_HOME=/app
WORKDIR $APP_HOME
RUN echo "Home is $APP_HOME" > info.txt
CMD cat info.txt

When you run the container, it prints Home is /app.

1.1. Using ENV in Subsequent Instructions

Variables are expanded in RUN, CMD, and ENTRYPOINT when using the shell form. In exec form, they are not automatically expanded; you need to invoke a shell explicitly or use the form ["sh", "-c", "command"].

Example of exec form with variable expansion:

dockerfile
CMD ["sh", "-c", "echo $APP_HOME"]

1.2. Overriding ENV at Runtime

You can override or add environment variables when starting a container using the -e or --env flag with docker run.

bash
docker run -e APP_HOME=/newpath myimage

This overrides the APP_HOME variable for that container only. You can also pass multiple -e flags or use an env file (--env-file).

1.3. Viewing Environment Variables in a Container

  • Inside a running container, run env or printenv.
  • Inspect the image or container: docker inspect shows the Config.Env field.

2. Build Arguments (ARG)

The ARG instruction defines a variable that users can pass at build time. Unlike ENV, ARG variables are not available in the running container unless you explicitly copy them into ENV.

Syntax:

dockerfile
ARG <name>[=<default value>]
  • You can set a default value; if not provided, the variable is optional.
  • To pass a value at build time: docker build --build-arg <name>=<value> -t myimage .

Example:

dockerfile
FROM alpine
ARG VERSION=latest
RUN echo "Building version ${VERSION}" > /version.txt

Build with default: docker build -t myapp . → version.txt contains "Building version latest". Build with custom: docker build --build-arg VERSION=1.2.3 -t myapp . → version.txt contains "1.2.3".

2.1. Scope of ARG

  • ARG variables are only available during the build, from the line where they are defined to the end of the build stage.
  • They are not persisted in the final image (unless used to set an ENV).

2.2. Predefined ARGs

Docker automatically provides a few built-in build arguments:

  • HTTP_PROXY / http_proxy
  • HTTPS_PROXY / https_proxy
  • FTP_PROXY / ftp_proxy
  • NO_PROXY / no_proxy
  • TARGETPLATFORM, TARGETOS, TARGETARCH, TARGETVARIANT (for multi-platform builds)
  • BUILDPLATFORM, etc.

These can be used without declaring them, useful in multi-stage builds.


3. Key Differences: ENV vs ARG

FeatureENVARG
ScopeImage and running containerBuild only (not in final image)
PersistenceYes, remains in imageNo, disappears after build
Override at buildNot directlyYes, with --build-arg
Override at runtimeYes, with -eNo
Use caseRuntime configuration, default settingsBuild-time parameters (versions, flags)

4. Combining ARG and ENV

A common pattern is to use an ARG to set an ENV variable. This allows the value to be customized at build time and also persist into the container.

dockerfile
FROM alpine
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
RUN echo "App version: $APP_VERSION" > /version.txt

Now:

  • Build with --build-arg APP_VERSION=2.0 → the container will have APP_VERSION=2.0 set.
  • The variable is available at runtime and can still be overridden with -e.

INFO

Important: The ENV instruction is evaluated at build time. The value of ARG is substituted into ENV at that moment.


5. Using Environment Variables in Commands

5.1. In Shell Form

dockerfile
ENV NAME=John
RUN echo "Hello, $NAME" > /greeting

5.2. In Exec Form

Exec form does not invoke a shell, so variable expansion does not happen automatically. You have two options:

  • Use the shell form for CMD/ENTRYPOINT (less secure for signals, but simple).
  • Explicitly invoke a shell:
    dockerfile
    CMD ["sh", "-c", "echo $NAME"]

5.3. Using ENV in COPY and ADD

WARNING

Environment variables are not expanded in COPY or ADD instructions. You cannot do COPY $APP_HOME /something.


6. Best Practices and Security

DANGER

Never store secrets (passwords, API keys) in ENV or ARG. They can be inspected via docker history and docker inspect. Use Docker secrets (in Swarm) or external secret management.

  • Use ARG for non-sensitive build parameters like versions, flags.
  • Use ENV for runtime configuration that might change per environment.
  • Provide default values for ARG where possible to make builds predictable.
  • Combine ARG and ENV carefully to avoid leaking build-time values into the final image unnecessarily.

Example of a Sensitive Leak

dockerfile
ARG SECRET_TOKEN
ENV SECRET_TOKEN=${SECRET_TOKEN}

If you then push this image, anyone pulling it can see SECRET_TOKEN in docker inspect. Never do this with real secrets.


7. Hands-On Tasks

Task 1: Basic ENV Usage

  1. Create a Dockerfile:
    dockerfile
    FROM alpine
    ENV GREETING="Hello from Docker"
    CMD echo $GREETING
  2. Build and run: docker build -t greet . && docker run greet
  3. Override at runtime: docker run -e GREETING="Custom hello" greet
  4. Check the environment inside the container (optional): docker run -it greet sh and run env.

Task 2: Build Arguments

  1. Create a Dockerfile that accepts an argument VERSION and creates a file with the version.
    dockerfile
    FROM alpine
    ARG VERSION=1.0
    RUN mkdir /app && echo "Version: $VERSION" > /app/version.txt
    CMD cat /app/version.txt
  2. Build with default: docker build -t version-app . and run.
  3. Build with custom version: docker build --build-arg VERSION=2.5 -t version-app2 . and run.
  4. Run docker history version-app2 and observe that the ARG does not create a persistent layer? (It does create a layer, but the variable is not in the final image.)

Task 3: Combine ARG and ENV

  1. Create a Dockerfile that accepts APP_ENV as a build argument and sets it as an environment variable.
    dockerfile
    FROM alpine
    ARG APP_ENV=production
    ENV APP_ENV=${APP_ENV}
    RUN echo "Environment: $APP_ENV" > /env.txt
    CMD cat /env.txt
  2. Build for development: docker build --build-arg APP_ENV=development -t myapp-dev .
  3. Run it: docker run myapp-dev → should print "Environment: development".
  4. Override at runtime: docker run -e APP_ENV=staging myapp-dev → prints "staging" (runtime override wins).

Task 4: Inspect Environment Variables

  1. Build any image with an ENV.

  2. Use docker inspect to view the environment:

    bash
    docker inspect <image> | grep -A 5 Env

    Or format:

    bash
    docker inspect --format='{{json .Config.Env}}' <image>
  3. For a running container, use docker exec <container> env.

Task 5: Multi-Platform ARG (Optional)

  1. If you have Docker Buildx set up, try building for multiple platforms and use TARGETPLATFORM in your Dockerfile:
    dockerfile
    FROM --platform=$BUILDPLATFORM alpine
    ARG TARGETPLATFORM
    RUN echo "Building for $TARGETPLATFORM" > /platform
    CMD cat /platform
  2. Build with docker buildx build --platform linux/amd64,linux/arm64 -t platform-test . (or just a single platform to see the variable).

Summary

Key Takeaways

  • ENV sets environment variables that persist into the running container and can be overridden at runtime.
  • ARG defines build-time variables that can be passed with --build-arg; they do not survive into the final image.
  • Use ARG for build customization (e.g., version numbers, proxy settings).
  • Use ENV for runtime configuration and default values.
  • Combine ARG and ENV to make build-time values available at runtime, but beware of leaking secrets.
  • Never store secrets in ENV or ARG if you plan to share the image.

Check Your Understanding

  1. What is the main difference between ENV and ARG in terms of persistence?
  2. If you set an environment variable with ENV in a Dockerfile, can you change it when running the container? How?
  3. How do you pass a build-time variable named GIT_COMMIT to docker build?
  4. Will a variable defined with ARG be available inside a running container? Why or why not?
  5. Why is it unsafe to use ARG or ENV for sensitive data like passwords?
  6. Write a Dockerfile instruction that sets a default build argument PORT to 8080.
Click to see answers
  1. ENV variables persist in the image and are available at runtime. ARG variables exist only during the build process and do not persist into the final image.
  2. Yes, use the -e or --env flag with docker run, e.g., docker run -e VAR_NAME=value myimage.
  3. With the --build-arg flag: docker build --build-arg GIT_COMMIT=<hash> -t myimage .
  4. No. ARG is only available during the build. Unless you copy its value into an ENV variable, it won't exist inside the running container.
  5. Both ARG and ENV values are visible in docker history and docker inspect. Anyone who pulls the image can read them. Even if you remove them in a later layer, the filesystem layers still contain the original data.
  6. ARG PORT=8080 or simply ARG PORT (without a default).

Additional Resources


Next Up

In the next lesson, we'll dive into multi-stage builds, a powerful technique to keep your images small and efficient. See you there!