Install nodejs and npm in Dockerfile

11,909

You're likely running into issues with cached layers. There's a long section in the Dockerfile best practices documentation on using apt-get. Probably worth a read.

The gist is that Docker doesn't recognize any difference between the first and second RUN apt-get update, nor does it know that apt-get install depends on a fresh apt-get update layer.

The solution is to combine all of that into a single RUN command (recommended) or disable the cache during the build process (docker build --no-cache).

RUN apt-get update -yq \
    && apt-get -yq install curl gnupg ca-certificates \
    && curl -L https://deb.nodesource.com/setup_12.x | bash \
    && apt-get update -yq \
    && apt-get install -yq \
        dh-autoreconf=19 \
        ruby=1:2.5.* \
        ruby-dev=1:2.5.* \
        nodejs

Edit: Running your Dockerfile locally, I noticed no output from the curl command. After removing the -s flag (fail silently), you can see it's failing due to not being able to verify the server's SSL certificate:

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

The solution to that issue is to install ca-certificates before running curl. I've updated the RUN command above.

Share:
11,909
Stefan
Author by

Stefan

Updated on July 18, 2022

Comments

  • Stefan
    Stefan almost 2 years

    The context

    I have a Dockerfile to create an image that contains an apache webserver. However I also want to build my website using the Dockerfile so that the build process isn't dependent on the developers local environment. Note that the docker container is only going to be used for local development not for production.

    The problem

    I have this Dockerfile:

    FROM httpd
    RUN apt-get update -yq
    RUN apt-get -yq install curl gnupg
    RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
    RUN apt-get update -yq
    RUN apt-get install -yq \
            dh-autoreconf=19 \
            ruby=1:2.5.* \
            ruby-dev=1:2.5.* \
            nodejs
    

    I build it:

    sudo docker build --no-cache .
    

    The build completes successfully, here is part of the output:

    Step 9/15 : RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
     ---> Running in e6c747221ac0
    ......
    ......
    ......
    Removing intermediate container 5a07dd0b1e01
     ---> 6279003c1e80
    Successfully built 6279003c1e80
    

    However, when I run the image in a container using this:

    sudo docker container run --rm -it --name=debug 6279003c1e80 /bin/bash
    

    Then when doing apt-cache policy inside the container, it doesn't show the repository that should have been added with the curl command. Also when doing apt-cache policy nodejs it shows the old version is installed.

    However when I then run the following inside the container:

    curl -sL https://deb.nodesource.com/setup_12.x | bash
    apt-cache policy
    apt-cache policy nodejs
    

    It shows me the repository is added and it shows the newer nodejs version is available.

    So why is it that when using the curl command using RUN inside the docker file it doesn't seem to work, but when doing it manually in the container from a shell then it does work? And how can I get around this problem?

    Updates

    • Note that to prevent caching issues I am using the --no-cache flag.
    • I also removed all containers and did sudo docker system prune and rebuild the image but without success.
    • I tried bundling everything in one RUN command as user "hmm" suggested (as this is best practice for apt commands):
    RUN apt-get update -yq \
        && apt-get -yq install curl gnupg && \
        && curl -sL https://deb.nodesource.com/setup_12.x | bash \
        && apt-get update -yq \
        && apt-get install -yq \
            dh-autoreconf=19 \
            ruby=1:2.5.* \
            ruby-dev=1:2.5.* \
            nodejs \
        && rm -rf /var/lib/apt/lists/*
    
  • Stefan
    Stefan almost 4 years
    Thanks for the link I will have a look to that! I don't think the caching is the problem though since I am already using the --no-cache flag. I even tried not to use docker-compose so using: sudo docker build --no-cache . but that still gives me the same problem. And after running it in one RUN statement I still get the old node version installed and the repo not being added.
  • chash
    chash almost 4 years
    @Stefan Sorry, I missed that you were already using --no-cache. See my edit for a potential solution.
  • Stefan
    Stefan almost 4 years
    Thanks a lot! I didn't realize I was silencing the output, I thought it was being executed successfully. I don't understand why sometimes the docker build fails when there is an error (like when using a shell comand that isn't installed) and other times it happily continues. Or is it because I was using -s for the curl command?
  • chash
    chash almost 4 years
    -s was hiding the error output, but the reason Docker continued is because the exit status of the curl https://<url> | bash command is actually the exit status of bash (0 in this case). To cause the pipeline to fail because of curl's non-zero exit status, you would need to tweak the default shell to e.g. bash and use set -o pipefail: SHELL ["/bin/bash", "-o", "pipefail", "-c"].