moby: Dockerfile CMD doesn't understand ENV variables

Similar to #1136.

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

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 9
  • Comments: 31 (2 by maintainers)

Commits related to this issue

Most upvoted comments

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.

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.

Have you tried this way?

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

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.

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

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

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

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.

@akravetz I recall on DockerConf it was said multiple times avoid using docker-entrypoint.sh, they grow massively.

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.

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

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 "$*"

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

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

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}'

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

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

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.

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