docker-compose
build-args
Docker
containerization
environment-variables

How to define build-args in docker-compose?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Compose, build arguments are defined under build.args and passed to ARG instructions in the Dockerfile. They are useful for build-time choices such as selecting a base image tag, a tool version, or a build mode.

The important distinction is build-time versus runtime. args affect image creation. environment affects the running container.

Define ARG in the Dockerfile First

Compose can only pass values to arguments that your Dockerfile knows about. For example:

dockerfile
1FROM node:20-alpine
2
3ARG APP_ENV=development
4ARG APP_VERSION=dev
5
6WORKDIR /app
7COPY package.json package-lock.json ./
8RUN npm ci
9
10COPY . .
11RUN echo "Building ${APP_NAME:-app} version ${APP_VERSION} for ${APP_ENV}"
12
13CMD ["npm", "start"]

The important lines are the ARG instructions. Without them, Compose has nowhere to send the build arguments.

Pass Build Args in Compose

Now define them in compose.yaml or docker-compose.yml:

yaml
1services:
2  web:
3    build:
4      context: .
5      dockerfile: Dockerfile
6      args:
7        APP_ENV: production
8        APP_VERSION: "1.2.3"

Then build:

bash
docker compose build web

That passes the two values into the Docker build for the web service.

Mapping Form and List Form

Compose accepts args in two common forms. The mapping form is the clearest:

yaml
args:
  APP_ENV: production
  APP_VERSION: "1.2.3"

You may also see list form:

yaml
args:
  - APP_ENV=production
  - APP_VERSION=1.2.3

Both work. The mapping form is usually easier to read and maintain.

Pass Values From the Shell or .env

Compose variable interpolation lets you pull values from your environment:

yaml
1services:
2  web:
3    build:
4      context: .
5      args:
6        APP_ENV: ${APP_ENV}
7        APP_VERSION: ${APP_VERSION:-dev}

If APP_ENV is exported in the shell or defined in a .env file, Compose substitutes it. The :-dev part provides a default when the variable is missing.

This is useful for CI pipelines where the build version comes from the job environment.

Multi-Stage Builds Still Use the Same Mechanism

Build args are especially common in multi-stage Dockerfiles:

dockerfile
1ARG NODE_IMAGE=node:20-alpine
2FROM ${NODE_IMAGE} AS build
3
4WORKDIR /app
5COPY package.json package-lock.json ./
6RUN npm ci
7COPY . .
8RUN npm run build
9
10FROM nginx:alpine
11COPY --from=build /app/dist /usr/share/nginx/html

Then in Compose:

yaml
1services:
2  web:
3    build:
4      context: .
5      args:
6        NODE_IMAGE: node:22-alpine

That lets you vary the builder image without editing the Dockerfile.

Build Args Are Not Secrets

This is the biggest misconception. Build args do not behave like a secure secret store. They may appear in build metadata, logs, or image history depending on how they are used.

So avoid using them for real secrets such as:

  • API tokens
  • private keys
  • database passwords

For secrets, use dedicated secret mechanisms or inject values at runtime through a safer path.

ARG Versus ENV

A good rule:

  • use ARG for values needed only while building
  • use ENV for values the container should have when it runs

Example runtime environment:

yaml
1services:
2  web:
3    environment:
4      APP_ENV: production

That is separate from build.args. Mixing them up is a very common source of confusion.

Common Pitfalls

The most common mistake is defining args in Compose but forgetting the matching ARG instructions in the Dockerfile.

Another is expecting a build arg to exist when the container starts. It will not, unless you explicitly copy it into an ENV or a file during the build.

Teams also pass secrets through build args because it feels convenient. That is usually a security mistake.

Finally, do not assume changing a build arg is free. It can invalidate Docker's build cache for later layers, which may be exactly what you want or exactly what you were trying to avoid.

Summary

  • Define build-time variables with ARG in the Dockerfile.
  • Pass values from Compose with build.args.
  • Use environment interpolation for CI and per-environment builds.
  • Treat build args as build-time inputs, not runtime environment variables.
  • Do not use build args for real secrets.

Course illustration
Course illustration

All Rights Reserved.