david
david

Reputation: 6785

How can I use a variable inside a Dockerfile CMD?

Inside my Dockerfile:

ENV PROJECTNAME mytestwebsite
CMD ["django-admin", "startproject", "$PROJECTNAME"]

Error:

CommandError: '$PROJECTNAME' is not a valid project name

What is the quickest workaround here? Does Docker have any plan to "fix" or introduce this functionality in later versions of Docker?

NOTE: If I remove the CMD line from the Docker file and then run the Docker container, I am able to manually run django-admin startproject $PROJECTNAME from inside the container and it will create the project...  

Upvotes: 277

Views: 191885

Answers (6)

Asimandia
Asimandia

Reputation: 528

You also can use exec This is the only known way to handle signals and use env vars simultaneously. It can be helpful while trying to implement something like graceful shutdown according to Docker github

Example:

ENV PROJECTNAME mytestwebsite 
CMD exec django-admin startproject $PROJECTNAME

Upvotes: 50

Zana Simsek
Zana Simsek

Reputation: 99

For the Java developers, following my solution below gonna work:

if you tried to run your container with a Dockerfile like below

ENTRYPOINT ["/docker-entrypoint.sh"]
# does not matter your parameter $JAVA_OPTS wrapped as ${JAVA_OPTS}
CMD ["java", "$JAVA_OPTS", "-javaagent:/opt/newrelic/newrelic.jar", "-server", "-jar", "app.jar"]

with an ENTRYPOINT shell script below:

#!/bin/bash
set -e
source /work-dir/env.sh
exec "$@"

it will build the image correctly but print the error below during the run of container:

Error: Could not find or load main class $JAVA_OPTS
Caused by: java.lang.ClassNotFoundException: $JAVA_OPTS

instead, Java can read the command line parameters either through the command line or by _JAVA_OPTIONS environment variable. so, it means we can pass the desired command line parameters through _JAVA_OPTIONS without changing anything on Dockerfile as well as to allow it to be able to start as parent process of container for the valid docker signalization via exec "$@".

The below one is my final version of the Dockerfile and docker-entrypoint.sh files:

...
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["java", "-server", "-jar", "app.jar"]
#!/bin/bash
set -e
source /work-dir/env.sh
export _JAVA_OPTIONS="-XX:+PrintFlagsFinal"
exec "$@"

and after you build your docker image and tried to run it, you will see the logs below that means it worked well:

Picked up _JAVA_OPTIONS: -XX:+PrintFlagsFinal
[Global flags]
      int ActiveProcessorCount                     = -1                                        {product} {default}

Upvotes: 3

csomakk
csomakk

Reputation: 5497

Inspired on above, I did this:

#snapshot by default. 1 is release.
ENV isTagAndRelease=0

CMD     echo is_tag: ${isTagAndRelease} && \
        if [ ${isTagAndRelease} -eq 1 ]; then echo "release build"; mvn -B release:clean release:prepare release:perform; fi && \
        if [ ${isTagAndRelease} -ne 1 ]; then echo "snapshot build"; mvn clean install; fi && \ 
       .....

Upvotes: 0

rex roy
rex roy

Reputation: 1099

If you want to use the value at runtime, set the ENV value in the Dockerfile. If you want to use it at build-time, then you should use ARG.

Example :

ARG value
ENV envValue=$value
CMD ["sh", "-c", "java -jar ${envValue}.jar"]

Pass the value in the build command:

docker build -t tagName --build-arg value="jarName"

Upvotes: 60

Flavio
Flavio

Reputation: 1705

Lets say you want to start a java process inside a container:

Example Dockerfile excerpt:

ENV JAVA_OPTS -XX +UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm 
... 
ENTRYPOINT ["/sbin/tini", "--", "entrypoint.sh"] 
CMD ["java", "${JAVA_OPTS}", "-myargument=true"]

Example entrypoint.sh excerpt:

#!/bin/sh 
... 
echo "*** Startup $0 suceeded now starting service using eval to expand CMD variables ***"
exec su-exec mytechuser $(eval echo "$@")

Upvotes: 13

larsks
larsks

Reputation: 311606

When you use an execution list, as in...

CMD ["django-admin", "startproject", "$PROJECTNAME"]

...then Docker will execute the given command directly, without involving a shell. Since there is no shell involved, that means:

  • No variable expansion
  • No wildcard expansion
  • No i/o redirection with >, <, |, etc
  • No multiple commands via command1; command2
  • And so forth.

If you want your CMD to expand variables, you need to arrange for a shell. You can do that like this:

CMD ["sh", "-c", "django-admin startproject $PROJECTNAME"]

Or you can use a simple string instead of an execution list, which gets you a result largely identical to the previous example:

CMD django-admin startproject $PROJECTNAME

Upvotes: 386

Related Questions