Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

docker-compose build environment variable

TL;DR : How can I pass env variable when building the image with docker-compose and have docker run image command recognize them ?

I have this Dockerfile :

FROM mhart/alpine-node:10
ADD . /app
WORKDIR /app
RUN apk --no-cache add --virtual builds-deps build-base python &&\
    yarn global add nodemon &&\
    yarn &&\
    apk del builds-deps build-base python

and this docker-compose.yml :

version: "3.3"
services:

  api:
    build:
      context: .
      dockerfile: Dockerfile-preprod
    image: registry.git.louis-girones.fr:4567/make-your-night/back:preprod
    environment:
      - NODE_ENV=development
    env_file:
      - .env
    volumes:
      - .:/app
    ports:
      - "${PORT_PREPROD}:${PORT_PREPROD}"
    command: sh -c "mkdir -p dist && touch ./dist/app.js && yarn run start"

  mongo:
    image: mongo:4.0
    ports:
      - "${MONGO_PREPROD}"
    command: mongod
    volumes:
      - ./data:/data/db

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    volumes:
      - ./esdata:/usr/share/elasticsearch/data
    environment:
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - discovery.type=single-node
    ports:
      - "9300:9300"
      - "9200:9200"

volumes:
  esdata:

With this .env file (which is in the root folder, like docker-compose.yml and Dockerfile) :

#!/usr/bin/env bash

NODE_ENV=development
PORT=9000
SECRET_SESSION=superSecr3t
APP_NAME=Night Vision
API_VERSION=/api/v0/
DEFAULT_TZ=Europe/Paris
ASSETS_URI=http://localhost:9000/public/img/
BCRYPT_WORKFACTOR=1
ES_PORT=9200
ES_LOG_LEVEL=trace

And this code in the node server startup :

// Export the config object based on the NODE_ENV
// ==============================================
const config: IConfig = commonConfig

if (commonConfig.env === 'development') {
    _.merge(config, developmentConfig)
} else if (commonConfig.env === 'test') {
    _.merge(config, testConfig)
} else if (commonConfig.env === 'preproduction') {
    _.merge(config, preproductionConfig)
} else if (commonConfig.env === 'production') {
    _.merge(config, productionConfig)
} else {
    throw new Error('Please set an environment')
}

When I run the docker-compose build command, everything is fine, but for instance If I try docker run myimage yarn run test the Error "Please set an environment" is thrown.

I would expect that

env_file:
  - .env

makes the env variables of this file accessible in my image but that is not the case, that's why I tried to add

 environment:
  - NODE_ENV=development

But still no success, I have also tried to pass my env variable as command line argument when I run the build :

docker-compose build --build-arg NODE_ENV=development api

But it gives me this message :

[Warning] One or more build-args [NODE_ENV] were not consumed
Successfully built 9b14dd5abc3f

And I would really prefer to use the first or second methods

docker version : 18.06.1-ce docker-compose version : 1.19.0

like image 941
L. Faros Avatar asked Sep 20 '18 17:09

L. Faros


People also ask

Can docker compose use env variables?

Docker Compose allows us to pass environment variables in via command line or to define them in our shell. However, it's best to keep these values inside the actual Compose file and out of the command line.

How pass env variable in docker compose command?

Set environment variable with the --env-file flag If you want to pass a file containing all of your environment variables to a Docker container, you can use the --env-file flag. The --env-file flag allows you to pass a file containing environment variables to a Docker container.

How do I set an environment variable in docker?

Use -e or --env value to set environment variables (default []). If you want to use multiple environments from the command line then before every environment variable use the -e flag. Note: Make sure put the container name after the environment variable, not before that.

Does docker compose override environment variables?

But docker-compose does not stop at the . env and the host's current environment variables. It's cool that you can simply override values of your . env file, but this flexibility is can also be the source of nasty bugs.

How to use environment variables with docker compose?

Which is similar to what you can use docker run --env-file=FILE ... for load the environment file when running a docker image. You can put environment variables inside .env file and it will be picked up automatically from docker compose. This pretty cool feature as there are some interesting usages within the docker compose file itself.

How does compose work with docker-compose?

Compose uses the variable values from the shell environment in which docker-compose is run. For example, suppose the shell contains POSTGRES_VERSION=9.3 and you supply this configuration: When you run docker-compose up with this configuration, Compose looks for the POSTGRES_VERSION environment variable in the shell and substitutes its value in.

What is the path of the docker compose project directory?

The .env file path is as follows: Project directory can be explicitly defined with the --file option or COMPOSE_FILE environment variable. Otherwise, it is the current working directory where the docker compose command is executed ( +1.28 ). For previous versions, it might have trouble resolving .env file with --file or COMPOSE_FILE.

What is the difference between build arguments and env arguments in Docker?

Build arguments works on the BUILD phase, it has no impact on the RUN phase of docker whereas the ENV can be used at both phases. Environment variables declared in Dockerfile are available at both the phases but environment variables declared in the environment file or passed in the RUN command are available at only the RUN phase.


2 Answers

There's a note in the docs:

Note: If your service specifies a build option, variables defined in environment are not automatically visible during the build. Use the args sub-option of build to define build-time environment variables.

It's also described in here. First (as mentioned above), you need to specify ARG in Dockerfile:

FROM mhart/alpine-node:10
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
ADD . /app
WORKDIR /app
RUN apk --no-cache add --virtual builds-deps build-base python &&\
    yarn global add nodemon &&\
    yarn &&\
    apk del builds-deps build-base python

And then edit your docker-compose file to include the argument during build, like so:

build:
  context: .
  dockerfile: Dockerfile-preprod
  args:
    - NODE_ENV=development

Or even

build:
  context: .
  dockerfile: Dockerfile-preprod
  args:
    - NODE_ENV=${NODE_ENV}
like image 176
Alex Karshin Avatar answered Oct 22 '22 02:10

Alex Karshin


As per documentation under build 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.

args:
  NODE_ENV:

Additionally, it will use the .env file as documented under variable substitution.

You can set default values for environment variables using a .env file, which Compose automatically looks for. Values set in the shell environment override those set in the .env file.

So you can create a .env file like below.

NODE_ENV=production
BASE_URL=/foo/bar/

And then just list them in the compose file either under build.args to make them available on build, or under environment to make them available on run.

version: '3.9'
services:
  app:
    build:
      context: .
      # these are available on build
      args:
        NODE_ENV:
    # these are available on run
    environment:
      BASE_URL:

This is kind of useful. However, the problem that comes along with this approach is that if the variables are not set from shell or env file, the default values in the Dockerfile will be overwritten with an empty value.

If one wants to keep some sort of default, the following can be used.

version: '3.9'
services:
  app:
    build:
      context: .
      args:
        NODE_ENV: ${NODE_ENV:-dev}
    environment:
      BASE_URL: ${BASE_URL:-http://localhost:8080}

This is making usage of posix parameter expansion. If the variable is not set, it will use the value after :-. So in the example above, it would default to NODE_ENV=dev and BASE_URL=http://localhost:8080, if those are not set.
Allowing to override them with a .env file or by setting a shell variable, i.e. export NODE_ENV=prod.

If you want to change the env file it's using, you can do that with the --env-file flag.

docker compose --env-file .my-env up
like image 42
The Fool Avatar answered Oct 22 '22 03:10

The Fool