Weird behaviour passing build-args to Dockerfile through docker-compose

Viewed 606

I'm facing a strange problem (or better: two different, weird problems) trying to pass build-args to my Dockerfile through docker-compose up.

My files - initial setup

Dockerfile:

ARG  NODE_VERSION
FROM node:${NODE_VERSION}

ARG NPM_REGISTRY_TOKEN
RUN echo "=====> token ${NPM_REGISTRY_TOKEN}"
... ... ...

docker-compose.yml:

version: '3'
services:
  myservice:
    build:
      context: ./myservice
      dockerfile: ../Dockerfile
      args:
        - NODE_VERSION=10.15.1-alpine
        - NPM_REGISTRY_TOKEN

With this initial setup in place, I have the following behaviour (on Linux Mint 20, docker-compose version 1.26.2, build eefe0d31):

  • running docker build --build-arg NPM_REGISTRY_TOKEN=xyz123 produces in output =====> token xyz123: the NPM_REGISTRY_TOKEN arg flows to the Dockerfile
  • running docker-compose build --build-arg NPM_REGISTRY_TOKEN=xyz123 myservice produces in output =====> token xyz123: the NPM_REGISTRY_TOKEN arg flows to the Dockerfile
  • running NPM_REGISTRY_TOKEN=xyz123 docker-compose up myservice produces in output =====> token : the NPM_REGISTRY_TOKEN env arg should flow to the Dockerfile due to - NPM_REGISTRY_TOKEN (according to https://docs.docker.com/compose/compose-file/#args: You can omit the value when specifying a build argument, in which case its value at build time is the value in the environment where Compose is running) but it seems to not be available during build

My files - reloaded

Simply changing my docker-compose.yml file to

version: '3'
services:
  myservice:
    build:
      context: ./myservice
      args:
        - NODE_VERSION=10.15.1-alpine
        - NPM_REGISTRY_TOKEN
      dockerfile: ../Dockerfile

seems to solve the problem: switching args and dockerfile entries in yml file unlocks the capability to pass environment variables to Dockerfile as build-args through docker-compose up, too. Problem solved. Or not?

Changing OS, getting new problem

So, developers in my team use a bunch of different operating systems: Linux, Mac Os, and Windows, too. Running the same commands on the same version (1.26.2) of docker-compose on Windows 10 Professional 1909 we're getting the same problem we faced initially, both using the initial version of the docker-compose.yml file and using the version that works on Linux. We tried passing env var from command line, setting them in the command prompt, setting them as system variables through GUI... we tried launching docker-compose up for git-bash, too, but we're not able to get the variable value in Dockerfile.

I googled a bit aaround but I've not found any reference to known bugs or limitation of the Windows version of docker-compose.

Anyone have any idea what the problem might be? Thank you very much in advance!

1 Answers

So, finally, after some try-and-fail on different OSs and with different configurations, I ended up with an explanation of my problem - and therefore with a viable workaround, which allowed me to reach a satisfactory configuration for my docker-compose-yml file.

Short answer: it wasn't a matter of OSs nor env var passing nor order of context / dockerfile sections - it was a matter of clash between different services in my compose file.

More in detail: my docker-compose.yml file contained an additional service, too, whose job was to initialize the database the application was pointing to:

version: '3'
services:
  myservice:
    build:
      context: ./myservice
      dockerfile: ../Dockerfile
      args:
        - NODE_VERSION=10.15.1-alpine
        - NPM_REGISTRY_TOKEN
    depends_on:
      - persistence
      - db_initializer
    command: sh -c './wait-for localhost:5432 -- ./wait-for localhost:15672 -- npm run start:dev'
  persistence:
    # Setting up the DBMS here
  db_initializer:
    build:
      context: ./myservice
      dockerfile: ../Dockerfile
      args:
        - NODE_VERSION=10.15.1-alpine
    depends_on:
      - persistence
    command: sh -c './wait-for localhost:5432 -- ./wait-for localhost:15672 -- npm run db:migrate'

So, the problem was that I was configuring two services based on the same, self-build image, launching it with different commands (npm run db:migrate for the db_initializer service, npm run start:dev for the application service). Apparently compose took the configuration provided for the first initialized service (db_initializer, because myservice was dependant on it) and used that configuration for both services, ignoring the (different) args section I was providing for the second container: so I was able to solve (this time really!) the problem simply merging services declaration, including all args I needed:

version: '3'
services:
  myservice:
    build:
      context: ./myservice
      dockerfile: ../Dockerfile
      args:
        - NODE_VERSION=10.15.1-alpine
        - NPM_REGISTRY_TOKEN
    depends_on:
      - persistence
      - db_initializer
    command: sh -c './wait-for localhost:5432 -- ./wait-for localhost:15672 -- run db:migrate && npm run start:dev'
  persistence:
    # Setting up the DBMS here

So, after a bunch of months without collecting answers, I think it's time to share my experience, hoping it can help someone encountering this weird behaviour.

Related