369

I'm building a container for a ruby app. My app's configuration is contained within environment variables (loaded inside the app with dotenv).

One of those configuration variables is the public ip of the app, which is used internally to make links. I need to add a dnsmasq entry pointing this ip to 127.0.0.1 inside the container, so it can fetch the app's links as if it were not containerized.

I'm therefore trying to set an ENV in my Dockerfile which would pass an environment variable to the container.

I tried a few things.

ENV REQUEST_DOMAIN $REQUEST_DOMAIN
ENV REQUEST_DOMAIN `REQUEST_DOMAIN`

Everything passes the "REQUEST_DOMAIN" string instead of the value of the environment variable though. Is there a way to pass environment variables values from the host machine to the container?

Damien MATHIEU
  • 31,924
  • 13
  • 86
  • 94

10 Answers10

569

You should use the ARG directive in your Dockerfile which is meant for this purpose.

The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg <varname>=<value> flag.

So your Dockerfile will have this line:

ARG request_domain

or if you'd prefer a default value:

ARG request_domain=127.0.0.1

Now you can reference this variable inside your Dockerfile:

ENV request_domain=$request_domain

then you will build your container like so:

$ docker build --build-arg request_domain=mydomain Dockerfile


Note 1: Your image will not build if you have referenced an ARG in your Dockerfile but excluded it in --build-arg.

Note 2: If a user specifies a build argument that was not defined in the Dockerfile, the build outputs a warning:

[Warning] One or more build-args [foo] were not consumed.

Yair Kukielka
  • 10,686
  • 1
  • 38
  • 46
Daniel van Flymen
  • 10,931
  • 4
  • 24
  • 39
  • 16
    ARG is available from docker v1.9 onwards. – Synesso May 20 '16 at 06:02
  • is this supported to be built remotely on docker repo? – James Lin Oct 06 '16 at 23:13
  • 6
    ```Your image will not build if you have referenced an ARG in your Dockerfile but excluded it in --build-arg``` you are wrong. You can build image with references even without --build-arg. Moreover you can set default value for the build arg. – ALex_hha Mar 23 '17 at 11:20
  • 6
    What is the purpose of the ENV line? For me --build-arg + ARG was enough. – Phil Sep 29 '17 at 07:43
  • 16
    The ARG defines a variable to be used inside the dockerfile in subsequent commands. The ENV defines an environment variable which is passed to the container. – herm Oct 30 '17 at 10:52
  • Why is there `=` in `ENV request_domain=$request_domain`? I thought the syntax would be like `ENV request_domain $request_domain`. BTW, correct me if I am wrong, but why doesn't `ENV request_domain=$request_domain` (or `ENV request_domain $request_domain`) just work? Isn't it passing the value of `request_domain` to docker? – ytu Jul 23 '18 at 08:18
  • This will not work for AWS_REGION environment variable – Unbreakable Sep 12 '19 at 02:31
  • 2
    Please note that while on both Windows and Linux, the Dockerfile itself will expect variables with the `$var` syntax... When using `RUN` commands on Windows, cmd will still expect the `%var%` syntax and powershell will still expect the `$env:var` syntax. – cowlinator May 05 '20 at 21:49
  • I think you want to point the build command at a directory, like `.`. Not `Dockerfile`. At least, that's what worked for me, and is what is described here: https://stackoverflow.com/a/43297404/1196465 – David Gay Aug 05 '20 at 14:15
  • https://stackoverflow.com/questions/72216766/how-do-i-use-docker-environment-variable-in-entrypoint-array-issue-facing-in-ku?noredirect=1#comment127596994_72216766 Please help me to sort this out – Cyril I May 13 '22 at 06:45
69

So you can do: cat Dockerfile | envsubst | docker build -t my-target -

Then have a Dockerfile with something like:

ENV MY_ENV_VAR $MY_ENV_VAR

I guess there might be a problem with some special characters, but this works for most cases at least.

jonasfj
  • 2,349
  • 2
  • 24
  • 22
  • 14
    This doesn't seem to work if you need to ADD files from the directory containing the Dockerfile. – Tom Hennen Jul 22 '14 at 13:19
  • 3
    Very nice solution! On a Mac you can get `envsubst` as part of `brew install gettext`. But because of possible conflicts with the BSD build system it is "keg-only" and no symlnks are made. However, it is safe to do `ln -s /usr/local/Cellar/gettext/*/bin/envsubst /usr/local/bin/` to add that one command to your PATH. (It's really the libs that are the concern.) Or you can use it in its `/usr/local/Cellar/gettext/*/bin/envsubst` location – Bruno Bronosky Mar 11 '15 at 08:02
  • 2
    To clarify @TomHennen's comment, piping the Dockerfile to `docker build -` is, specifically, what doesn't work when you reference relative paths from your Dockerfile, regardless of env var substitution. – superEb Apr 15 '15 at 14:09
  • 5
    Re @TomHennen's comment, if you _do_ want to use context-dependent commands like COPY in your Dockerfile you could always redirect the output of envsubst to a temporary file and then feed that into `docker build` instead. Example: `cat Dockerfile | envsubst > DockerfileWithEnvVars`, then `docker build -t my-target -f DockerfileWithEnvVars .`, then `rm DockerfileWithEnvVars` – snark May 19 '15 at 13:05
  • Or you can use sponge from moreutils package ```envsubst < Dockerfile | sponge Dockerfile``` – ALex_hha Mar 23 '17 at 11:17
  • Adding `-f` before `-` seem to fix the issue with the `ADD` command not working: `cat Dockerfile | envsubst | docker build -t my-target . -f -` – Daniel Feb 24 '19 at 02:16
  • If you per the instructions of @ALex_hha use sponge, you'll overwrite the original Dockerfile. Probably not what you want. – raahlb Nov 06 '20 at 13:18
  • Please help me to sort this out https://stackoverflow.com/questions/72216766/how-do-i-use-docker-environment-variable-in-entrypoint-array-issue-facing-in-ku?noredirect=1#comment127596994_72216766 – Cyril I May 13 '22 at 06:49
57

This is for those looking to pass env variable from docker-compose using .env file to dockerfile during build and then pass those args as env variable to container. Typical docker-compose file

services:
  web:
    build:
      context: ./api
      dockerfile: Dockerfile
      args:
        - SECRET_KEY=$SECRET_KEY
        - DATABASE_URL=$DATABASE_URL
        - AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID

Pass the env variable present in .env file to args in build command. Typical .env file

SECRET_KEY=blahblah
DATABASE_URL=dburl

Now when you run docker-compose up -d command, docker-compose file takes values from .env file then pass it to docker-compose file. Now Dockerfile of web containes all those varibales through args during build. Now typical dockerfile of web,

FROM python:3.6-alpine

ARG SECRET_KEY
ARG DATABASE_URL
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ARG AWS_BUCKET
ARG AWS_REGION
ARG CLOUDFRONT_DOMAIN

ENV CELERY_BROKER_URL redis://redis:6379/0
ENV CELERY_RESULT_BACKEND redis://redis:6379/0
ENV C_FORCE_ROOT true
ENV SECRET_KEY  ${SECRET_KEY?secretkeynotset}
ENV DATABASE_URL ${DATABASE_URL?envdberror}

Now we recieved those secret_key and db url as arg in dokcerfile. Now let's use those in ENV as ENV SECRET_KEY ${SECRET_KEY?secretkeynotset}. Now even docker container has those variables in it's environment. Remember not to use ARG $SECRET_KEY(which I did). It should be ARG SECRET_KEY

Zincfan
  • 739
  • 5
  • 7
  • It does not work `The Compose file './docker-compose.yaml' is invalid because: Unsupported config option for services.web: 'args'` – Maxim Colesnic Sep 16 '21 at 06:08
  • 4
    @MaximColesnic your args should be under build section, and not under web which your error says. So args is under services.web.build. Watch your indentations. – Zincfan Sep 16 '21 at 11:54
  • 1
    Thank you for this very hard to find information. Exactly what I needed. – benibilme Nov 01 '21 at 05:54
  • 2
    thanks for this well defined answer – Shan-Desai Nov 08 '21 at 17:09
  • Please help me to sort this out https://stackoverflow.com/questions/72216766/how-do-i-use-docker-environment-variable-in-entrypoint-array-issue-facing-in-ku?noredirect=1#comment127596994_72216766 – Cyril I May 13 '22 at 06:49
  • Saved the day! Thanks :) – Roman Kozin Sep 29 '22 at 22:17
  • This doesn't seem to work with docker-compose 3.7. ``` jetty: build: jetty args: - CURRENT_PROFILE=$CURRENT_PROFILE - ENV_CONFIG_PATH=$ENV_CONFIG_PATH``` It shows and error - mapping values are not allowed here in 'reader', line 20, column 16: args: ^ – Gautam Jun 08 '23 at 13:21
24

An alternative using envsubst without losing the ability to use commands like COPY or ADD, and without using intermediate files would be to use Bash's Process Substitution:

docker build -f <(envsubst < Dockerfile) -t my-target .
tshepang
  • 12,111
  • 21
  • 91
  • 136
  • 4
    unfortunately that does not seem to work (Docker 17.09), I get the error `unable to prepare context: the Dockerfile (/dev/fd/63) must be within the build context` – Alexander Klimetschek Oct 31 '17 at 00:52
  • https://stackoverflow.com/questions/72216766/how-do-i-use-docker-environment-variable-in-entrypoint-array-issue-facing-in-ku?noredirect=1#comment127596994_72216766 Please help me to sort this out – Cyril I May 13 '22 at 06:49
17

Load environment variables from a file you create at runtime.

export MYVAR="my_var_outside"
cat > build/env.sh <<EOF
MYVAR=${MYVAR}
EOF

... then in the Dockerfile

ADD build /build
RUN /build/test.sh

where test.sh loads MYVAR from env.sh

#!/bin/bash
. /build/env.sh
echo $MYVAR > /tmp/testfile
Dan C
  • 171
  • 1
  • 2
  • 1
    Remember to give permissions to the shell file. with `chmod 775 env.sh` – tblev Jan 18 '22 at 20:22
  • https://stackoverflow.com/questions/72216766/how-do-i-use-docker-environment-variable-in-entrypoint-array-issue-facing-in-ku?noredirect=1#comment127596994_72216766 Please help me to sort this out – Cyril I May 13 '22 at 06:49
6

If you just want to find and replace all environment variables ($ExampleEnvVar) in a Dockerfile then build it this would work:

envsubst < /path/to/Dockerfile | docker build -t myDockerImage . -f -

snassr
  • 1,228
  • 14
  • 10
  • Best answer here, and based on that, was able to do: `envsubst < ./Dockerfile | docker build -squash -t ${DOCKIMG}:${VERSION} . -f -` where the `FROM` line is using the environment variable. – mikequentel May 21 '20 at 20:54
2

When using build-arg...

docker build --build-arg CODE_VERSION=1.2 Dockerfile

...consider that the variable is not availabe after FROM:

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}

An ARG declared before a FROM is outside of a build stage, so it can’t be used in any instruction after a FROM.

Generally ARGs should be placed after FROM if not required during FROM:

FROM base:xy
ARG  ABC=123

To use the default value of an ARG declared before the first FROM use an ARG instruction without a value inside of a build stage:

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact

goulashsoup
  • 2,639
  • 2
  • 34
  • 60
0

You can directly pass env variables when container start :

docker run -e [your_variable_name = your_variable_value] [image to instanciate]

Example :

docker run --rm -e TARGET=192.168.1.9 ubuntu env

The output is :

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=36399469e519
TARGET=192.168.1.9
HOME=/root

The target variable was added to your env variables of the current container !

Khaled Boussoffara
  • 1,567
  • 2
  • 25
  • 53
0

Correct me if I wrong. The question was about env variables - not about args. The question was How to send ENV var to container - it means that container can reference to that variable. So I think the answer should be the following:

  1. there is no need to use ARG cos it is used only during building the image
  2. if you need to pass environment variable to container there is syntax that can be used to run the container: docker run --name [container-name] -e "[variable-name]=[new-value]" [image-name]
Maksym
  • 1
-3

add -e key for passing environment variables to container. example:

$ MYSQLHOSTIP=$(sudo docker inspect -format="{{ .NetworkSettings.IPAddress }}" $MYSQL_CONRAINER_ID)
$ sudo docker run -e DBIP=$MYSQLHOSTIP -i -t myimage /bin/bash

root@87f235949a13:/# echo $DBIP
172.17.0.2
Valentin Kantor
  • 1,799
  • 1
  • 23
  • 27