How do I use Docker environment variable in ENTRYPOINT array?
Asked Answered
V

11

230

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.

Vapid answered 19/6, 2016 at 6:49 Comment(0)
B
381

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\!"
Barrier answered 19/6, 2016 at 7:13 Comment(10)
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?Selina
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 ./greetingAutonomy
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)Autonomy
Note that the shell form can cause signals to not be passed through to the process (greeting in your example). hynek.me/articles/docker-signalsCologne
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 wayMansoor
@Cologne agreed. What if we use ENTRYPOINT [ "sh", "-c", "echo $HOME" ]? Does this also have the same issue of signals not being propagated?Sweptback
@Barrier - any reason NOT to use the shell form? Why bother with the exec form?Eliott
@Eliott many containers don't have a shell, and it can interfere with your applicationScheffler
This response assumes that /bin/sh is available, which is not the case with an image based on scratch. Is there any way to do it with the exec form?Terpene
Note: Surround this variable with double quotes; otherwise, it can lead to unexpected behaviorGyrostabilizer
D
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

Devisable answered 27/5, 2019 at 10:18 Comment(0)
R
30

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"]
Reneareneau answered 17/4, 2018 at 23:57 Comment(6)
it seems your answer doesn't provide complete solutions the OP's questionBranum
I suppose I don't understand how it doesn't provide a solution to the OP's question ... I updated with an example to solve with the exact question in mind.Reneareneau
you mentioned still you ran into some issues!!Branum
correct, which is why I introduced a new solution. ... the "accepted" answer didn't work for me, so I echo out to a shell script and that worked.Reneareneau
So writing the script to a file inside the dockerfile is the correct way to go about it? I think not.Indictable
I'd be interested in hearing your approach then @ReverendTim ;)Reneareneau
W
16

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.

Wallache answered 23/9, 2021 at 14:0 Comment(3)
Just in case, it's possible to have ENV customizable: ARG TEST; ENV TEST="${TEST:-foo}". Related: docs.docker.com/compose/environment-variables/… gnu.org/software/bash/manual/html_node/…Koeppel
This assumes that /bin/sh is available. :(Terpene
The relevant Docker documentation: docs.docker.com/reference/dockerfile/#shell-and-exec-formCoreycorf
I
15

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 --.

P.S. -- also works with any POSIX-compliant shell. See Utility Syntax Guides

Isagogics answered 22/4, 2021 at 2:47 Comment(8)
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.Brinkmanship
@Brinkmanship what kind of signals will not be received?Isagogics
@smac98 According to ENTRYPOINT docs, 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. Also, please also note that single quotes are not allowed for ENTRYPOINT array elements, they need to be double-quoted.Brinkmanship
@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?Isagogics
@Brinkmanship this behavior is no different from a script that is started with just bash as the parent, without execIsagogics
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.Brinkmanship
This assumes that /bin/bash is available. :(Terpene
@Terpene At the time of writing, yes, I assumed bash because that's what I was using and that's where I discovered a solution. In case you're referring to the use of --, that's a standard posix compliant shell option. See Utility Syntax Guidelines and this question. As long as the shell you are using is posix-compliant, you are free to assume this answer works with it.Isagogics
P
3

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
Philo answered 10/3, 2021 at 11:36 Comment(2)
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 hynek.me/articles/docker-signalsNadler
This assumes that /bin/sh is available. :(Terpene
E
2

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"]
Educatory answered 29/3, 2023 at 12:42 Comment(1)
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.Psalm
B
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.

Brader answered 28/4, 2021 at 6:3 Comment(0)
P
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
Pyromania answered 4/1, 2022 at 6:58 Comment(1)
This assumes that /bin/sh is available. :(Terpene
R
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

Redon answered 15/2, 2022 at 22:35 Comment(0)
E
-2

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

Elsieelsinore answered 17/6, 2023 at 3:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.