dockernext.jsdocker-compose

Next.js docker compose - passing environment variable during build time


Next.js app bundles the env variables during build time. That's why I need to pass env variables as args and used them during the build stage. Now as part of the CD/CI pipeline I want to pass args dynamically via environment variables declared in compose.yaml. However, the env variable from compose file are not read during build time. In fact, the terminal prints the following message:

time="2024-12-12T23:35:10-05:00" level=warning msg="The "NEXT_PUBLIC_GRAPHQL_SERVER" variable is not set. Defaulting to a blank string."

Compose.yaml

services:
  frontend:
    build:
      context: ./frontend
      args:
        - NEXT_PUBLIC_GRAPHQL_SERVER=${NEXT_PUBLIC_GRAPHQL_SERVER}
    env_file:
      - ./frontend/.env
    environment:
      NODE_ENV: production
      NEXT_PUBLIC_GRAPHQL_SERVER: http://localhost:3000/
    ports:
      - 8080:8080

dockerfile:

FROM base AS builder
WORKDIR /usr/src/app

COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_GRAPHQL_SERVER
ENV NEXT_PUBLIC_GRAPHQL_SERVER=${NEXT_PUBLIC_GRAPHQL_SERVER}
RUN npm run build

All works fine when I explicitly set the env variable in args but that's not the desired workflow.

services:
  frontend:
    build:
      context: ./frontend
      args:
        - NEXT_PUBLIC_GRAPHQL_SERVER=http://localhost:3000/

Solution

  • You need to distinguish between the environment(s) you are preparing for the build, for the container runtime, and for compose itself.

    Docker compose itself, when it is first parsing the compose.yml file does not use the env_file: to expand any ${VAR} directives in the compose.yml - that directive is for injecting variables into containers.

    As such, dockers own environment is the source of variables: A .env file in the same folder as the compose.yml, any environment variables that are exported prior to executing docker compose are the sources for variable expansion in the compose.yml.