222

If I set an environment variable, say ENV ADDRESSEE=world, and I want to use it in the entry point script concatenated into a fixed string like:

ENTRYPOINT ["./greeting", "--message", "Hello, world!"]

with world being the value of the environment varible, how do I do it? I tried using "Hello, $ADDRESSEE" but that doesn't seem to work, as it takes the $ADDRESSEE literally.

Psycho Punch
  • 6,418
  • 9
  • 53
  • 86

11 Answers11

367

You're using the exec form of ENTRYPOINT. Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen. For example, ENTRYPOINT [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: ENTRYPOINT [ "sh", "-c", "echo $HOME" ].
When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.(from Dockerfile reference)

In your case, I would use shell form

ENTRYPOINT ./greeting --message "Hello, $ADDRESSEE\!"
Nils
  • 25
  • 5
vitr
  • 6,766
  • 8
  • 30
  • 50
  • 4
    `ENTRYPOINT java -jar /dockertest.jar -Djava.security.egd=file:/dev/./urandom -Dserver.port=$port` while `ENV port=123`. The port ENV is not resolved. Any ideas why? – xetra11 Aug 30 '17 at 18:19
  • 4
    While it works, it appears to create some new problems, like not including the passed arguments to that entrypoint. For example you can't add a `--attitude "shouting"` argument to the `docker run` command which should get passed to `./greeting` – Daniel F Nov 28 '18 at 23:48
  • 13
    Use `ENTRYPOINT ./greeting --message "Hello, $ADDRESSEE\! $0 $@"` if you also want to pass additional variables to `./greeting` via the `docker run` invocation (or to pass the `CMD` of the Dockerfile) – Daniel F Nov 29 '18 at 00:06
  • 10
    Note that the shell form can cause signals to not be passed through to the process (`greeting` in your example). https://hynek.me/articles/docker-signals/ – jbg Jul 26 '19 at 08:48
  • can I get the value of an environment variable in exec form , like `[myexecutable.sh , "$variable"] , I can do it by ["sh", "-c" , "echo $var"] , but not in this way – lazarus Mar 27 '20 at 16:32
  • @jbg agreed. What if we use `ENTRYPOINT [ "sh", "-c", "echo $HOME" ]`? Does this also have the same issue of signals not being propagated? – Hmerac Nov 04 '21 at 16:33
  • @vitr - any reason NOT to use the shell form? Why bother with the exec form? – jtlz2 Jul 05 '22 at 07:58
  • 1
    @jtlz2 many containers don't have a shell, and it can interfere with your application – Brandon Jul 12 '23 at 08:19
34

After much pain, and great assistance from @vitr et al above, i decided to try

  • standard bash substitution
  • shell form of ENTRYPOINT (great tip from above)

and that worked.

ENV LISTEN_PORT=""

ENTRYPOINT java -cp "app:app/lib/*" hello.Application --server.port=${LISTEN_PORT:-80}

e.g.

docker run --rm -p 8080:8080 -d --env LISTEN_PORT=8080 my-image

and

docker run --rm -p 8080:80 -d my-image

both set the port correctly in my container

Refs

see https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html

mlo55
  • 6,663
  • 6
  • 33
  • 26
28

I tried to resolve with the suggested answer and still ran into some issues...

This was a solution to my problem:

ARG APP_EXE="AppName.exe"
ENV _EXE=${APP_EXE}

# Build a shell script because the ENTRYPOINT command doesn't like using ENV
RUN echo "#!/bin/bash \n mono ${_EXE}" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh

# Run the generated shell script.
ENTRYPOINT ["./entrypoint.sh"]

Specifically targeting your problem:

RUN echo "#!/bin/bash \n ./greeting --message ${ADDRESSEE}" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Ben Kauffman
  • 396
  • 4
  • 5
14

For me, I wanted to store the name of the script in a variable and still use the exec form.

Note: Make sure, the variable you are trying to use is declared an environment variable either from the commandline or via the ENV directive.

Initially I did something like:

ENTRYPOINT [ "${BASE_FOLDER}/scripts/entrypoint.sh" ]

But obviously this didn't work because we are using the shell form and the first program listed needs to be an executable on the PATH. So to fix this, this is what I ended up doing:

ENTRYPOINT [ "/bin/bash", "-c", "exec ${BASE_FOLDER}/scripts/entrypoint.sh \"${@}\"", "--" ]

Note the double quotes are required

What this does is to allow us to take whatever extra args were passed to /bin/bash, and supply those same arguments to our script after the name has been resolved by bash.


man 7 bash

--
A -- signals the end of options and disables further option processing. Any arguments after the -- are treated as filenames and arguments. An argument of - is equivalent to --.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • 5
    This works great. Without `"--"` bash will steal the first argument and never give it to the script, and `"--"` makes things work as expected. However, two improvements. 1) The command should start with `exec`: `"exec ${BASE_FOLDER}/..."`. Otherwise the script/executable won't receive signals properly. 2) It should be `\"${@}\"` and not just `${@}` for proper handling quoted arguments that are passed to CMD. – Mike May 18 '21 at 16:12
  • @Mike what kind of signals will not be received? – smac89 May 19 '21 at 04:04
  • 4
    @smac98 According to [ENTRYPOINT docs](https://docs.docker.com/engine/reference/builder/#entrypoint), if shell is parent process, it will not propagate `SIGTERM` to your script when `docker stop ...` is ran. You can search for more info on "docker pid 1 signal", or read, for example, about it in [Google's best practices](https://cloud.google.com/architecture/best-practices-for-building-containers?hl=tr#solution_1_run_as_pid_1_and_register_signal_handlers). Also, please also note that single quotes are not allowed for `ENTRYPOINT` array elements, they need to be double-quoted. – Mike May 19 '21 at 08:02
  • @Mike, I don't think adding `exec` will fix that problem. If a script contains the following: `sleep 10;echo foo | tee --append /tmp/foo.out` (don't forget the `#!/bin/bash`), I would expect that if we run this script with `exec` as you suggested, then immediately press CTRL+Z i.e. `SIGSTOP`, that the sleep command will stop as well, but this is not the case. The `sleep` command keeps running, and if you wait long enough before resuming the script, after it resumes, it will not keep sleeping, but immediately print `foo`. Maybe I'm missing something here, but is that what you expect? – smac89 Jun 03 '21 at 13:15
  • @Mike this behavior is no different from a script that is started with just bash as the parent, without `exec` – smac89 Jun 03 '21 at 13:16
  • 2
    The case you described is when `bash` started from `ENTRYPOINT` execs into another `bash` which runs your script. If we assume bash has issues dealing with signals properly, it should not be used as the actual process that's tested. Secondly, I'm not sure what platform you're using, normally Ctrl-Z sends `SIGTSTP`, not `SIGSTOP`. In order to really test what happens you need to cause `SIGTERM` signal which is generated when you run `docker stop`. – Mike Jun 03 '21 at 14:33
14

I SOLVED THIS VERY SIMPLY!

IMPORTANT: The variable which you wish to use in the ENTRYPOINT MUST be ENV type (and not ARG type).

EXAMPLE #1:

ARG APP_NAME=app.jar                    # $APP_NAME can be ARG or ENV type.
ENV APP_PATH=app-directory/$APP_NAME    # $APP_PATH must be ENV type.
ENTRYPOINT java -jar $APP_PATH

This will result with executing: java -jar app-directory/app.jar

EXAMPLE #2 (YOUR QUESTION):

ARG ADDRESSEE="world"                       # $ADDRESSEE can be ARG or ENV type.
ENV MESSAGE="Hello, $ADDRESSEE!"            # $MESSAGE must be ENV type.
ENTRYPOINT ./greeting --message $MESSAGE

This will result with executing: ./greeting --message Hello, world!

  • Please verify to be sure, whether you need quotation-marks "" when assigning string variables.

MY TIP: Use ENV instead of ARG whenever possible to avoid confusion on your part or the SHELL side.

  • Just in case, it's possible to have ENV customizable: `ARG TEST; ENV TEST="${TEST:-foo}"`. Related: https://docs.docker.com/compose/environment-variables/#substitute-environment-variables-in-compose-files https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html – Artfaith May 29 '22 at 01:21
4

In my case worked this way: (for Spring boot app in docker)

ENTRYPOINT java -DidMachine=${IDMACHINE} -jar my-app-name

and passing the params on docker run

docker run --env IDMACHINE=Idmachine -p 8383:8383 my-app-name
Rob Sedgwick
  • 4,342
  • 6
  • 50
  • 87
Scoreby
  • 81
  • 4
  • Just be aware, that when you use the shell-Form, your Java-Process will not receive the SIGTERM signal, since this is sent to the PID 1. And the Process with the PID=1 is the Shell process. Therefore, your Java-Process will only get killed brutally with a SIGKILL and cannot shutdown gracefully. See https://hynek.me/articles/docker-signals/ – aubium77 Oct 20 '21 at 09:03
1

Here is what worked for me:

ENTRYPOINT [ "/bin/bash", "-c", "source ~/.bashrc && ./entrypoint.sh ${@}", "--" ]

Now you can supply whatever arguments to the docker run command and still read all environment variables.

Pavel Tankov
  • 429
  • 1
  • 4
  • 12
1

I solved the problem using a variation on "create a custom script" approach above. Like this:

FROM hairyhenderson/figlet
ENV GREETING="Hello"
RUN printf '#!/bin/sh\nfiglet -W \${GREETING} \$@\n' > /runme && chmod +x /runme
ENTRYPOINT ["/runme"]
CMD ["World"]

Run like

docker container run -it --rm -e GREETING="G'Day" dockerfornovices/figlet-greeter Alec
Alec the Geek
  • 1,970
  • 14
  • 14
1

If someone wants to pass an ARG or ENV variable to exec form of ENTRYPOINT then a temp file created during image building process might be used.

In my case I had to start the app differently depending on whether the .NET app has been published as self-contained or not. What I did is I created the temp file and I used its name in the if statement of my bash script.

Part of my dockerfile:

ARG SELF_CONTAINED=true #ENV SELF_CONTAINED=true also works
# File has to be used as a variable as it's impossible to pass variable do ENTRYPOINT using Exec form. File name allows to check whether app is self-contained
RUN touch ${SELF_CONTAINED}.txt
COPY run-dotnet-app.sh .
ENTRYPOINT ["./run-dotnet-app.sh", "MyApp" ]

run-dotnet-app.sh:

#!/bin/sh

FILENAME=$1

if [ -f "true.txt" ]; then
   ./"${FILENAME}"
else
   dotnet "${FILENAME}".dll
fi

Łukasz Sypniewski
  • 602
  • 1
  • 10
  • 19
1

The previous answers suggest to use the shell form. In my case this was not an option as by using it the signals can't reach the process itself.

See point 1 here https://hynek.me/articles/docker-signals/

If the json syntax could resolve variables, this would be what I wanted:

ENTRYPOINT ["${APP_NAME}"]

If you create a file to run like this:

RUN echo "#!/bin/bash \n ${APP_NAME}" > ./entrypoint.sh

You lose the signals as well because a new shell will be created.

See point 2 and use exec

The final form that worked for me:

RUN echo "#!/bin/bash \n exec ${APP_NAME}" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • I got "exec ./entrypoint.sh: no such file or directory" on similar command: ```RUN echo "#!/bin/bash \n set -e \n geth --unlock 0x367103555b34Eb9a46D92833e7293D540bFd7143 --password /tmp/pwd.txt --keystore /tmp/keystore \n" > ./tmp/entrypoint.sh```, but when I put this into a separate file on the host machine and do ```COPY``` it works. Someone posted there is might be 'end of line' issue. – Gleichmut May 13 '23 at 11:16
-1

Came here looking for .envfile info for docker run commands and ended up finding the answer elsewhere, but thought I'd share for others:

This was key for understanding .envfile synax:

This file should use the syntax <variable>=value (which sets the variable to the given value) or (which takes the value from the local environment), and # for comments.

I found the this at https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file

In other words, don't do VAR1=$SOME_OTHER_ENV_VAR

Max Cascone
  • 648
  • 9
  • 25