node.jsdockerdockerfiledocker-cmd

Use docker run command to pass arguments to CMD in Dockerfile


I have a nodejs app can take two parameters when started. For example, I can use

node server.js 0 dev

or

node server.js 1 prod

to switch between production mode and dev mode and determine if it should turn the cluster on. Now I want to create docker image with arguments to do the similar thing. The only thing I can do so far is to adjust the Dockerfile to have a line

CMD [ "node", "server.js", "0", "dev"]

and

docker build -t me/app .
docker run -p 9000:9000 -d me/app

to build and run the docker image.

But If I want to switch to prod mode, I need to change the Dockerfile CMD to be:

CMD [ "node", "server.js", "1", "prod"]

and I need to kill the old one listening on port 9000 and rebuild the image. I wish I can have something like

docker run -p 9000:9000 environment=dev cluster=0 -d me/app 

to create an image and run the nodejs command with "environment" and "cluster" arguments, so I don't need to change the Dockerfile and rebuild the docker any more. How can I accomplish this?


Solution

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

    As noted by Pwnstar in the comments:

    When you use a shell command, this process will not be started with id=1.