Use docker run command to pass arguments to CMD in Dockerfile
Another option is to use ENTRYPOINT
to specify that node
is the executable to run and CMD
to provide the arguments. The docs have an example in Exec form ENTRYPOINT example.
Using this approach, your Dockerfile will look something like
FROM ...
ENTRYPOINT [ "node", "server.js" ]
CMD [ "0", "dev" ]
Running it in dev would use the same command
docker run -p 9000:9000 -d me/app
and running it in prod you would pass the parameters to the run command
docker run -p 9000:9000 -d me/app 1 prod
You may want to omit CMD
entirely and always pass in 0 dev
or 1 prod
as arguments to the run command. That way you don't accidentally start a prod container in dev or a dev container in prod.
The typical way to do this in Docker containers is to pass in environment variables:
docker run -p 9000:9000 -e NODE_ENV=dev -e CLUSTER=0 -d me/app
Option 1) Use ENV variable
Dockerfile
# we need to specify default values
ENV ENVIRONMENT=production
ENV CLUSTER=1
# there is no need to use parameters array
CMD node server.js ${CLUSTER} ${ENVIRONMENT}
Docker run
$ docker run -d -p 9000:9000 -e ENVIRONMENT=dev -e CLUSTER=0 -me/app
Option 2) Pass arguments
Dockerfile
# use entrypoint instead of CMD and do not specify any arguments
ENTRYPOINT node server.js
Docker run
Pass arguments after docker image name
$ docker run -p 9000:9000 -d me/app 0 dev
Make sure your Dockerfile declares an environment variable with ENV
:
ENV environment default_env_value
ENV cluster default_cluster_value
The ENV <key> <value>
form can be replaced inline.
Then you can pass an environment variable with docker run. Note that each variable requires a specific -e
flag to run.
docker run -p 9000:9000 -e environment=dev -e cluster=0 -d me/app
Or you can set them through your compose file:
node:
environment:
- environment=dev
- cluster=0
Your Dockerfile CMD
can use that environment variable, but, as mentioned in issue 5509, you need to do so in a sh -c
form:
CMD ["sh", "-c", "node server.js ${cluster} ${environment}"]
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 executed directly.
Same idea with Builder RUN (applies to CMD
as well):
Unlike the shell form, the exec form does not invoke a command shell.
This means that normal shell processing does not happen.
For example,
RUN [ "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:RUN [ "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.