bashdockerfiledocker-cmd

Why does CMD work while RUN not when building Docker image?


I have this project:

script.sh:

#!/bin/sh
echo "test"

Dockerfile:

FROM ubuntu
RUN ["sh", "script.sh"]

With build command docker build -t test ., I get this error:

 => [internal] load .dockerignore                                                                                  0.1s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 118B                                                                               0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                   2.3s
 => [auth] library/ubuntu:pull token for registry-1.docker.io                                                      0.0s
 => CACHED [1/2] FROM docker.io/library/ubuntu@sha256:ec050c32e4a6085b423d36ecd025c0d3ff00c38ab93a3d71a460ff1c44f  0.0s
 => ERROR [2/2] RUN ["sh", "script.sh"]                                                                            0.9s
------
 > [2/2] RUN ["sh", "script.sh"]:
#0 0.791 sh: 0: cannot open script.sh: No such file
------
Dockerfile:2
--------------------
   1 |     FROM ubuntu
   2 | >>> RUN ["sh", "script.sh"]
--------------------
ERROR: failed to solve: process "sh script.sh" did not complete successfully: exit code: 2

However if I change the Dockerfile to:

FROM ubuntu
CMD ["sh", "script.sh"]

Then it works:

[+] Building 1.2s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 73B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                   1.0s
 => CACHED [1/1] FROM docker.io/library/ubuntu@sha256:ec050c32e4a6085b423d36ecd025c0d3ff00c38ab93a3d71a460ff1c44f  0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:d9bf124a17e507c73f6defde7b046fff153fb464c2cad8a57333a0966ddf07c1                       0.0s
 => => naming to docker.io/library/test                                                                            0.0s

From Difference between RUN and CMD in a Dockerfile:

RUN - command triggers while we build the docker image.

CMD - command triggers while we launch the created docker image.

Still, I can't explain why RUN doesn't work but CMD does.


Solution

  • Docker doesn't implicitly COPY contents into the container, so with your current code, your container doesn't contain any script.sh file at all.

    Consider instead:

    FROM ubuntu
    COPY ./script.sh .
    RUN ["./script.sh"]
    

    Because you aren't running sh, the script's choice of interpreter (#!/bin/sh, #!/usr/bin/bash, #!/usr/bin/env zsh, #!/usr/bin/env python, or whatever else it might want) will be honored. (It's good practice to drop the .sh extension, so if you later rewrite to a non-shell language you won't need to choose between changing the calling convention and having a misleading filename).

    Note that this does require your script to have executable permissions and a valid shebang line; you may need to use chmod +x if it does not.