How to Use Multiple Build Arguments in Docker Build
TLDR: Pass multiple build arguments to docker build by repeating the --build-arg flag for each variable: docker build --build-arg VAR1=value1 --build-arg VAR2=value2 -t myapp .. Define these arguments in your Dockerfile with ARG instructions, optionally with default values. Build args are available only during build time, not at runtime - use ENV for runtime variables.
Build arguments let you customize Docker images at build time without hardcoding values. You can pass configuration like version numbers, API endpoints, or feature flags that affect how the image is built.
Basic Syntax
In your Dockerfile, declare arguments with ARG:
FROM node:18
# Declare build arguments
ARG NODE_ENV
ARG API_URL
ARG VERSION
# Use them during build
RUN echo "Building for environment: $NODE_ENV"
RUN echo "API URL: $API_URL"
RUN echo "Version: $VERSION"
# Copy and install dependencies
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
Then pass values when building:
docker build \
--build-arg NODE_ENV=production \
--build-arg API_URL=https://api.example.com \
--build-arg VERSION=1.2.3 \
-t myapp:1.2.3 \
.
Each --build-arg flag provides a value for one argument. You can have as many as you need.
Default Values
Provide default values in the Dockerfile so the build works even without explicit arguments:
FROM python:3.11
# Arguments with defaults
ARG PYTHON_ENV=development
ARG WORKERS=4
ARG PORT=8000
# These will use defaults if not provided
RUN echo "Environment: $PYTHON_ENV"
RUN echo "Workers: $WORKERS"
RUN echo "Port: $PORT"
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# You can use build args in CMD as well, but they become hardcoded
CMD ["gunicorn", "--workers", "${WORKERS}", "--bind", "0.0.0.0:${PORT}", "app:app"]
Build without arguments (uses defaults):
docker build -t myapp:dev .
# Uses: PYTHON_ENV=development, WORKERS=4, PORT=8000
Build with custom values:
docker build \
--build-arg PYTHON_ENV=production \
--build-arg WORKERS=8 \
--build-arg PORT=80 \
-t myapp:prod \
.
Build Args vs Environment Variables
Build arguments (ARG) are only available during the build process. If you need values at runtime, convert them to environment variables:
FROM ubuntu:22.04
# Build-time argument
ARG VERSION=1.0.0
# Convert to runtime environment variable
ENV APP_VERSION=$VERSION
# Build-time argument for installation
ARG INSTALL_DEV_TOOLS=false
# Conditionally install based on build arg
RUN if [ "$INSTALL_DEV_TOOLS" = "true" ]; then \
apt-get update && apt-get install -y vim curl git; \
fi
# This ENV is available at runtime
ENV NODE_ENV=production
CMD ["echo", "App version: $APP_VERSION"]
# Build with dev tools
docker build --build-arg INSTALL_DEV_TOOLS=true -t myapp:dev .
# Build without dev tools (production)
docker build --build-arg INSTALL_DEV_TOOLS=false -t myapp:prod .
# Run and see the version
docker run myapp:dev
# Output: App version: 1.0.0
Real-World Example: Multi-Environment Build
Here's a practical example for building different environments:
FROM node:18-alpine AS base
# Build arguments
ARG BUILD_DATE
ARG VERSION=latest
ARG ENVIRONMENT=development
# Install dependencies
WORKDIR /app
COPY package*.json ./
# Development stage
FROM base AS development
ARG NODE_ENV=development
ENV NODE_ENV=$NODE_ENV
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
# Production stage
FROM base AS production
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["npm", "start"]
# Use the stage specified by ENVIRONMENT arg
FROM ${ENVIRONMENT} AS final
# Add metadata using build args
LABEL version="${VERSION}"
LABEL build-date="${BUILD_DATE}"
LABEL environment="${ENVIRONMENT}"
EXPOSE 3000
Build for development:
docker build \
--build-arg ENVIRONMENT=development \
--build-arg VERSION=1.0.0-dev \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
-t myapp:dev \
.
Build for production:
docker build \
--build-arg ENVIRONMENT=production \
--build-arg VERSION=1.0.0 \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
-t myapp:1.0.0 \
.
Using Build Args for Dependency Versions
Lock down versions at build time:
FROM ubuntu:22.04
# Version arguments
ARG PYTHON_VERSION=3.11
ARG NODE_VERSION=18
# Install specific versions
RUN apt-get update && \
apt-get install -y \
python${PYTHON_VERSION} \
nodejs=${NODE_VERSION}.* \
&& rm -rf /var/lib/apt/lists/*
# Verify versions
RUN python${PYTHON_VERSION} --version && \
node --version
# Build with specific versions
docker build \
--build-arg PYTHON_VERSION=3.10 \
--build-arg NODE_VERSION=16 \
-t myapp:py3.10-node16 \
.
Build Args from File
For many arguments, use a file instead of repeating --build-arg:
Create build.args:
VERSION=1.5.0
ENVIRONMENT=staging
API_URL=https://staging-api.example.com
DATABASE_URL=postgresql://localhost/stagingdb
CACHE_ENABLED=true
MAX_CONNECTIONS=100
Reference it in the build (not directly supported, but you can script it):
# Read from file and build (bash)
while IFS='=' read -r key value; do
BUILD_ARGS="$BUILD_ARGS --build-arg $key=$value"
done < build.args
docker build $BUILD_ARGS -t myapp:staging .
Or use a build script:
#!/bin/bash
# build.sh
docker build \
--build-arg VERSION=1.5.0 \
--build-arg ENVIRONMENT=staging \
--build-arg API_URL=https://staging-api.example.com \
--build-arg DATABASE_URL=postgresql://localhost/stagingdb \
--build-arg CACHE_ENABLED=true \
--build-arg MAX_CONNECTIONS=100 \
-t myapp:staging \
.
chmod +x build.sh
./build.sh
Conditional Logic with Build Args
Use build arguments to enable/disable features:
FROM golang:1.21-alpine
ARG ENABLE_CGO=0
ARG ENABLE_RACE_DETECTOR=false
ARG BUILD_TAGS=""
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Build with conditional flags
RUN CGO_ENABLED=${ENABLE_CGO} \
go build \
$([ "$ENABLE_RACE_DETECTOR" = "true" ] && echo "-race") \
-tags="${BUILD_TAGS}" \
-o /app/server \
.
CMD ["/app/server"]
# Build with CGO enabled
docker build --build-arg ENABLE_CGO=1 -t myapp:cgo .
# Build with race detector
docker build --build-arg ENABLE_RACE_DETECTOR=true -t myapp:race .
# Build with custom tags
docker build --build-arg BUILD_TAGS="json1,fts5" -t myapp:sqlite .
Using Build Args in Multi-Stage Builds
Pass arguments to specific stages:
FROM node:18 AS builder
ARG BUILD_ENV=production
ARG API_KEY
# Build step uses API_KEY
RUN echo "Building with API key: ${API_KEY}"
COPY . .
RUN npm run build -- --env=${BUILD_ENV}
FROM nginx:alpine
ARG VERSION=unknown
# Copy build artifacts from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Add version label
LABEL version="${VERSION}"
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker build \
--build-arg BUILD_ENV=production \
--build-arg API_KEY=abc123 \
--build-arg VERSION=2.0.0 \
-t webapp:2.0.0 \
.
Note that API_KEY is only available in the builder stage and won't be in the final image - this is good for security.
Security Considerations
Build arguments can be viewed in the image metadata:
# Build with a "secret"
docker build --build-arg SECRET_KEY=my-secret -t myapp .
# View the build history - SECRET_KEY is visible!
docker history myapp
For actual secrets, use BuildKit secrets (Docker 18.09+):
# syntax=docker/dockerfile:1
FROM alpine
# Mount a secret (not a build arg)
RUN --mount=type=secret,id=mysecret \
cat /run/secrets/mysecret > /app/config
# Pass secret securely
docker build --secret id=mysecret,src=./secret.txt -t myapp .
The secret is never stored in the image layers.
Validating Build Arguments
Add validation to prevent invalid builds:
FROM ubuntu:22.04
ARG ENVIRONMENT
ARG VERSION
# Validate ENVIRONMENT
RUN if [ -z "$ENVIRONMENT" ]; then \
echo "ERROR: ENVIRONMENT build arg is required"; \
exit 1; \
fi
RUN if [ "$ENVIRONMENT" != "development" ] && \
[ "$ENVIRONMENT" != "staging" ] && \
[ "$ENVIRONMENT" != "production" ]; then \
echo "ERROR: ENVIRONMENT must be development, staging, or production"; \
exit 1; \
fi
# Validate VERSION format (basic check)
RUN if [ -z "$VERSION" ]; then \
echo "ERROR: VERSION build arg is required"; \
exit 1; \
fi
ENV APP_ENV=$ENVIRONMENT
ENV APP_VERSION=$VERSION
# This will fail
docker build -t myapp .
# ERROR: ENVIRONMENT build arg is required
# This will fail
docker build --build-arg ENVIRONMENT=testing -t myapp .
# ERROR: ENVIRONMENT must be development, staging, or production
# This works
docker build --build-arg ENVIRONMENT=production --build-arg VERSION=1.0 -t myapp .
Docker Compose with Build Args
Pass build arguments through Docker Compose:
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
args:
- NODE_ENV=production
- VERSION=1.0.0
- API_URL=https://api.example.com
ports:
- "3000:3000"
app-dev:
build:
context: .
args:
NODE_ENV: development
VERSION: 1.0.0-dev
API_URL: http://localhost:8080
ports:
- "3001:3000"
# Build with compose
docker-compose build app
# Or override from environment
NODE_ENV=staging VERSION=1.1.0 docker-compose build app
Checking What Build Args Are Available
To see what build arguments a Dockerfile expects:
# View the Dockerfile
grep "^ARG" Dockerfile
# Output:
# ARG NODE_ENV
# ARG VERSION=1.0.0
# ARG API_URL
Or inspect a built image:
docker history myapp --no-trunc
# Shows build commands including ARG values used
Build arguments are a clean way to customize Docker images at build time without maintaining multiple Dockerfiles. Use them for configuration that affects the build process, but remember they're baked into the image - for runtime configuration, use environment variables with docker run -e or Docker Compose.
Found an issue?