Keywords: Docker | Environment Variables | ARG Instruction | ENV Instruction | Container Build
Abstract: This technical article provides an in-depth analysis of environment variable management during Docker image builds, focusing on the fundamental differences between ARG and ENV instructions. Through comprehensive code examples and scenario analysis, it explains why ARG variables become invisible after build completion and how to properly use ENV instructions to make environment variables available at container runtime. The article also covers scope rules for build arguments, variable inheritance in multi-stage builds, and best practices for real-world development.
Overview of Environment Variables in Docker Builds
Environment variable management is a common yet frequently misunderstood aspect of Docker image construction. Many developers expect environment variables set via build arguments to remain available in the final container when using the docker build command, but reality often contradicts these expectations. This discrepancy primarily stems from insufficient understanding of the ARG and ENV instructions in Dockerfile.
Core Differences Between ARG and ENV
The ARG instruction defines build-time variables that are exclusively available during the image construction process. Once the image build completes, these variables do not appear in the final container's environment. Conversely, environment variables set with the ENV instruction are not only usable during the build process but also persist into the final image, making them available when containers are run.
Consider this typical erroneous example:
FROM ubuntu:latest
ARG TEST_ENV=something
Build command used:
docker build -t myimage --build-arg TEST_ENV="test" .
Checking environment variables after container execution:
docker exec containerid printenv
The result will not show the TEST_ENV variable, precisely because ARG variables have a lifecycle limited to the build process.
Correct Methods for Environment Variable Configuration
To make environment variables available at container runtime, the ENV instruction must be used. Here's the proper implementation approach:
FROM ubuntu:latest
ENV RUN_TIME=default_value
Alternatively, combine ARG and ENV to achieve build-time configurable environment variables:
FROM ubuntu:latest
ARG BUILD_TIME_ARG=default_build_value
ENV RUN_TIME_ENV=$BUILD_TIME_ARG
This approach allows setting the BUILD_TIME_ARG value via the --build-arg parameter during build, which then gets passed to the RUN_TIME_ENV environment variable, ultimately making it available in the container.
Practical Application Examples
Let's demonstrate the differences through a complete example:
FROM ubuntu:latest
# Build-time variable
ARG BUILD_TIME_VAR=build_default
# Runtime environment variable
ENV RUN_TIME_VAR=run_default
# Convert build-time variable to runtime variable
ENV CONVERTED_VAR=$BUILD_TIME_VAR
# Using variables during build process
RUN echo "Build-time variable value: $BUILD_TIME_VAR" > /build_info.txt
RUN echo "Runtime variable value: $RUN_TIME_VAR" >> /build_info.txt
RUN echo "Converted variable value: $CONVERTED_VAR" >> /build_info.txt
# Recording final environment state
RUN printenv > /final_env.txt
Build command:
docker build -t test-image --build-arg BUILD_TIME_VAR="custom_build_value" .
Running container and examining results:
docker run -it test-image cat /build_info.txt
docker run -it test-image cat /final_env.txt
docker run -it test-image printenv
From the output, we can clearly observe:
BUILD_TIME_VARis only available during build and doesn't appear in the final container's environment variablesRUN_TIME_VARandCONVERTED_VARare available both during build process and container runtime
Scope and Inheritance Rules
Understanding variable scope and inheritance rules becomes crucial in complex multi-stage build scenarios.
ARG Variables in Global Scope
ARG variables defined in the Dockerfile's global scope are not automatically inherited into individual build stages:
# Global scope
ARG GLOBAL_VAR=global_value
FROM ubuntu AS stage1
# GLOBAL_VAR inaccessible here
RUN echo "Stage 1: Attempting to access global variable" # Output empty
FROM ubuntu AS stage2
# Explicit declaration required for access
ARG GLOBAL_VAR
RUN echo "Stage 2: Global variable value: $GLOBAL_VAR"
Variable Inheritance Within Stages
Variables defined within a stage are automatically inherited by its child stages:
FROM ubuntu AS base
ARG STAGE_VAR=stage_default
ENV STAGE_ENV=env_default
FROM base AS child
# Can access variables defined in parent stage
RUN echo "Child stage - ARG: $STAGE_VAR"
RUN echo "Child stage - ENV: $STAGE_ENV"
Best Practice Recommendations
Based on practical development experience, we recommend the following best practices:
1. Clear Variable Purpose Definition
When writing Dockerfiles, clearly define the purpose of each variable:
- Use
ARGfor build-time configurations like version numbers, download URLs - Use
ENVfor runtime configurations like application settings, service ports
2. Security Considerations
ARG variable values are preserved in image history, making them unsuitable for sensitive information. For passwords, API keys, and other sensitive data, use Docker's secrets mechanism instead.
3. Default Value Configuration
Set reasonable default values for important ARG variables to ensure the build process functions correctly even without external parameters:
ARG APP_VERSION="1.0.0"
ARG NODE_ENV="production"
ENV NODE_ENV=$NODE_ENV
4. Combined Usage Scenarios
In real-world projects, combining ARG and ENV is often necessary:
# Build-time configuration
ARG APPLICATION_VERSION
ARG BUILD_TIMESTAMP
# Runtime environment
ENV APP_VERSION=$APPLICATION_VERSION
ENV BUILD_TIME=$BUILD_TIMESTAMP
ENV NODE_ENV=production
# Application-specific configuration
ENV DB_HOST=localhost
ENV DB_PORT=5432
Common Issues and Solutions
Issue 1: Variables Invisible in Container
Symptoms: Variables set via --build-arg are not visible when running the container.
Cause: Used ARG instead of ENV.
Solution: Use ENV instruction or pass ARG values through ENV.
Issue 2: Variable Loss in Multi-stage Builds
Symptoms: Unable to access variables defined in previous stages during subsequent build stages.
Cause: Incorrect understanding of variable scope rules.
Solution: Explicitly declare variables in each requiring stage, or ensure variables are defined in parent stages.
Issue 3: Unexpected Variable Value Overwriting
Symptoms: Environment variable values don't match expectations.
Cause: Possibly defined same-name variables in multiple locations.
Solution: Unify variable definition locations and avoid duplicate definitions.
Conclusion
Proper understanding and usage of environment variables during Docker build processes is an essential skill for containerized application development. While ARG and ENV share functional similarities, they possess fundamental differences: ARG serves build-time parameterization, while ENV addresses runtime environment configuration. Through judicious combination of these two instructions, developers can create Docker images that are both flexible and configurable, meeting deployment requirements across different environments.
In practical development, we recommend adhering to these principles: define clear variable purposes, set reasonable default values, prioritize security considerations, and understand scope rules. Only by following these guidelines can developers fully leverage the advantages of Docker environment variables and construct high-quality containerized applications.