Docker
Docker-Compose
Environment Variables
Containerization
DevOps

Pass environment variables from docker-compose to container at build stage

Master System Design with Codemia

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

Introduction

Docker Compose can pass configuration into two different phases: image build time and container runtime. The distinction matters because a value needed by RUN npm ci or a compiler step must be available while the image is being built, not only after the container starts.

Build-Time Variables Use ARG, Not environment

In a Compose file, the environment section configures the running container. It does not automatically expose values to the Dockerfile during docker compose build. For build-time values, use build.args in Compose and ARG in the Dockerfile.

yaml
1services:
2  app:
3    build:
4      context: .
5      dockerfile: Dockerfile
6      args:
7        APP_ENV: production
8        NODE_VERSION: "20"
9    environment:
10      APP_ENV: production
11    ports:
12      - "3000:3000"

The matching Dockerfile declares each build argument explicitly:

dockerfile
1ARG NODE_VERSION=20
2FROM node:${NODE_VERSION}-alpine
3
4ARG APP_ENV=development
5ENV APP_ENV=$APP_ENV
6
7WORKDIR /app
8COPY package*.json ./
9RUN npm ci
10COPY . .
11RUN npm run build
12
13CMD ["npm", "start"]

The important rule is simple. ARG exists during the image build. ENV exists in the final image and is available when the container runs. If you want a build argument to remain available later, copy it into an environment variable as shown above.

Supplying Values from a .env File

Compose can interpolate values from a local .env file. That keeps the Compose file reusable across development, staging, and production.

dotenv
APP_ENV=staging
NODE_VERSION=18
yaml
1services:
2  app:
3    build:
4      context: .
5      args:
6        APP_ENV: ${APP_ENV}
7        NODE_VERSION: ${NODE_VERSION}

When you run docker compose build, Compose resolves the placeholders and forwards them as build arguments. This is often enough for non-secret settings such as a language runtime version, feature flag, or package mirror URL.

When to Promote a Build Argument to ENV

Many developers only need the value while building. For example, a dependency installer may need PIP_INDEX_URL or a frontend build may need APP_ENV to select the correct bundle mode. In that case, leave it as ARG.

If the application also needs the value when the container starts, add a corresponding ENV line:

dockerfile
ARG API_BASE_URL
ENV API_BASE_URL=$API_BASE_URL

That pattern makes the value available in both phases. Be deliberate here. Every ENV line becomes part of the image metadata, so do not promote sensitive values without understanding the exposure.

A Practical Verification Workflow

One easy way to confirm that build arguments are wired correctly is to echo them during a build step:

dockerfile
ARG APP_ENV
RUN echo "Building for $APP_ENV"

Then run:

bash
docker compose build --no-cache
docker compose up

Using --no-cache avoids confusion when Docker reuses an earlier layer that was built with a different argument value. Once the build behaves as expected, remove noisy debug steps from the Dockerfile.

Common Pitfalls

The most common mistake is putting a variable under environment and expecting a RUN instruction to see it. That variable only exists for the started container, so the build still fails.

Another common issue is using an ARG in the FROM line without declaring it early enough. If a build argument is part of the base image tag, declare it before FROM. If you also need it later in the file, declare it again after FROM.

Caching also causes confusion. Docker may reuse a cached layer built with an older argument value. Rebuild with docker compose build --no-cache when you are debugging changed inputs.

Finally, do not pass secrets such as tokens or passwords with plain ARG unless you accept that they may appear in the image history or build logs. For sensitive data, prefer BuildKit secrets or another secret-management approach.

Summary

  • Use build.args in docker-compose.yml and ARG in the Dockerfile for build-time values.
  • Use environment for runtime container variables, not for RUN steps in the build.
  • Copy an ARG into ENV only when the container also needs the value after startup.
  • Use .env interpolation to keep Compose files portable across environments.
  • Avoid putting secrets in normal build arguments, and disable cache when debugging changed values.

Course illustration
Course illustration

All Rights Reserved.