Docker
CMD instruction
ARG
Dockerfile best practices
containerization

Is Docker ARG allowed within CMD instruction

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Docker ARG values are not directly available inside CMD or ENTRYPOINT instructions at runtime. ARG is a build-time variable that exists only during docker build. CMD runs at container start time, when ARG values are gone. To pass build-time values into runtime commands, copy the ARG into an ENV variable, which persists in the final image.

The Problem

dockerfile
ARG APP_PORT=8080
CMD ["echo", "$APP_PORT"]
bash
docker build --build-arg APP_PORT=3000 -t myapp .
docker run myapp
# Output: $APP_PORT    (literal string, not "3000")

The CMD instruction does not expand $APP_PORT because:

  1. The exec form ["echo", "$APP_PORT"] does no shell processing
  2. Even in shell form, ARG variables do not exist at runtime

Why ARG Does Not Work in CMD

 
1Build time:     ARG exists → used in RUN instructions
23Image created:  ARG values are discarded
45Runtime:        CMD executes → ARG is gone, ENV is available

ARG values are intentionally not baked into the image for security. You might pass secrets as build args that should not persist.

Fix: Copy ARG to ENV

dockerfile
1ARG APP_PORT=8080
2ENV APP_PORT=${APP_PORT}
3
4CMD echo "Running on port ${APP_PORT}"
bash
docker build --build-arg APP_PORT=3000 -t myapp .
docker run myapp
# Output: Running on port 3000

The ENV instruction captures the build-time ARG value and stores it in the image. CMD can then access it at runtime.

Exec Form vs Shell Form

dockerfile
1# Shell form: variable expansion works
2CMD echo "Port: ${APP_PORT}"
3# Docker runs: /bin/sh -c 'echo "Port: ${APP_PORT}"'
4
5# Exec form: NO variable expansion
6CMD ["echo", "Port: ${APP_PORT}"]
7# Docker runs: echo "Port: ${APP_PORT}" (literal string)
8
9# Exec form with shell: variable expansion works
10CMD ["sh", "-c", "echo Port: ${APP_PORT}"]
11# Docker runs: sh -c 'echo Port: 3000'

The exec form ["cmd", "arg"] does not invoke a shell, so environment variables are not expanded. Wrap in sh -c to get expansion.

Complete Example

dockerfile
1FROM node:20-alpine
2
3# Build-time arguments
4ARG NODE_ENV=production
5ARG APP_PORT=3000
6
7# Copy to runtime environment
8ENV NODE_ENV=${NODE_ENV}
9ENV APP_PORT=${APP_PORT}
10
11WORKDIR /app
12COPY package*.json ./
13RUN npm ci --only=production
14COPY . .
15
16EXPOSE ${APP_PORT}
17
18# CMD can now use the ENV values
19CMD ["sh", "-c", "node server.js --port ${APP_PORT}"]
bash
1# Build with custom values
2docker build --build-arg APP_PORT=8080 --build-arg NODE_ENV=development -t myapp .
3
4# Run: ENV values from build time are available
5docker run myapp
6# node server.js --port 8080
7
8# Override at runtime with -e
9docker run -e APP_PORT=9090 myapp
10# node server.js --port 9090

ARG Scope: Before and After FROM

dockerfile
1# ARG before FROM: only available for FROM
2ARG BASE_IMAGE=node:20-alpine
3FROM ${BASE_IMAGE}
4
5# This ARG is gone. Need to redeclare after FROM
6ARG APP_PORT=3000
7ENV APP_PORT=${APP_PORT}
8
9CMD echo "Port: ${APP_PORT}"

ARG declared before FROM is only available in the FROM instruction itself. Redeclare it after FROM to use it in subsequent instructions.

Multi-Stage Builds

dockerfile
1# Build stage
2FROM node:20 AS builder
3ARG BUILD_VERSION=1.0.0
4RUN echo "Building version ${BUILD_VERSION}"
5
6# Runtime stage: ARG from build stage is NOT available here
7FROM node:20-alpine
8ARG BUILD_VERSION=1.0.0
9ENV BUILD_VERSION=${BUILD_VERSION}
10CMD echo "Version: ${BUILD_VERSION}"

Each FROM starts a new build stage. ARG values from a previous stage do not carry over. Redeclare them in each stage where needed.

Using ENTRYPOINT with ARG

The same rules apply to ENTRYPOINT:

dockerfile
1ARG ENTRY_CMD=python
2ENV ENTRY_CMD=${ENTRY_CMD}
3
4# Shell form: works
5ENTRYPOINT ${ENTRY_CMD} app.py
6
7# Exec form: does NOT expand
8ENTRYPOINT ["${ENTRY_CMD}", "app.py"]
9
10# Exec form with shell: works
11ENTRYPOINT ["sh", "-c", "${ENTRY_CMD} app.py"]

Runtime Override with docker run -e

Even without ARG, you can pass values at runtime:

dockerfile
FROM alpine
# No ARG or ENV needed, set at runtime
CMD ["sh", "-c", "echo Hello ${NAME:-World}"]
bash
1docker run myapp
2# Hello World (default)
3
4docker run -e NAME=Alice myapp
5# Hello Alice

${NAME:-World} provides a default if NAME is not set.

Common Pitfalls

  • Using ARG directly in CMD: ARG values do not exist at runtime. Always copy to ENV first: ENV VAR=${ARG_VAR}.
  • Exec form does not expand variables: CMD ["echo", "$VAR"] prints the literal string $VAR. Use shell form or ["sh", "-c", "echo $VAR"].
  • ARG scope resets after FROM: Each build stage starts fresh. Redeclare ARG after every FROM where you need it.
  • Sensitive build args persisting in ENV: Copying a secret ARG to ENV bakes it into the image. Use Docker secrets or mount secrets at build time instead (--mount=type=secret).
  • Overriding CMD with docker run: docker run myapp /bin/sh replaces CMD entirely. Use ENTRYPOINT for the main command and CMD for default arguments if you need partial overrides.

Summary

  • ARG is build-time only. It does not exist when CMD or ENTRYPOINT run
  • Copy ARG to ENV to make build-time values available at runtime: ENV VAR=${ARG_VAR}
  • Shell form CMD echo $VAR expands variables; exec form CMD ["echo", "$VAR"] does not
  • Use ["sh", "-c", "command $VAR"] for variable expansion in exec form
  • ARG scope resets after each FROM in multi-stage builds
  • Use docker run -e VAR=value to override ENV values at runtime

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.