133

I was under the impression that environmental variables could be set on a single line as follows so as to minimize intermediary images.

FROM alpine:3.6
ENV RUBY_MAJOR 2.4 \
    RUBY_VERSION 2.4.1 \
    RUBY_DOWNLOAD_SHA256 4fc8a9992de3e90191de369270ea4b6c1b171b7941743614cc50822ddc1fe654 \
    RUBYGEMS_VERSION 2.6.12 \
    BUNDLER_VERSION 1.15.3

However, running a container based off of this snippet and calling # set |grep RU I see that the variables are not being assigned separately, but are combined into a single string.

RUBY_MAJOR='2.4     RUBY_VERSION 2.4.1     RUBY_DOWNLOAD_SHA256 4fc8a9992de3e90191de369270ea4b6c1b171b7941743614cc50822ddc1fe654     RUBYGEMS_VERSION 2.6.12     BUNDLER_VERSION 1.15.3'

However, if I explicitly set each variable as below, I get the expected output and there are no errors when calling the variables.

ENV RUBY_MAJOR 2.4
ENV RUBY_VERSION 2.4.1
ENV RUBY_DOWNLOAD_SHA256 4fc8a9992de3e90191de369270ea4b6c1b171b7941743614cc50822ddc1fe654
ENV RUBYGEMS_VERSION 2.6.12
ENV BUNDLER_VERSION 1.15.3

Question: Is it is possible to combine the setting of environment variables on a single line? If so, how would I do it? And is it a good practice?

1
  • 2
    Please, remember, one-liner ENV doesn't allow you to pass value of another env var defined before in the same line that the multi-line ENV does. E.g.: ENV RUBY_IMAGE_NAME=ruby RUBY_IMAGE_VERSION=2.4 RUBY_FULL_NAME=${RUBY_IMAGE_NAME}:${RUBY_IMAGE_VERSION} vs. ENV RUBY_IMAGE_NAME=ruby ENV RUBY_IMAGE_VERSION=2.4 ENV RUBY_FULL_NAME=${RUBY_IMAGE_NAME}:${RUBY_IMAGE_VERSION} Commented Apr 6, 2022 at 15:01

2 Answers 2

235

There are two formats for specifying environments. If you need single variable then you below format

ENV X Y

This will assign X as Y

ENV X Y Z

This will assign X as Y Z

If you need to assign multiple environment variables then you use the other format

ENV X=Y Z=A

This will assign X as Y and Z as A. So your Dockerfile should be

FROM alpine:3.6
ENV RUBY_MAJOR=2.4 \
    RUBY_VERSION=2.4.1 \
    RUBY_DOWNLOAD_SHA256=4fc8a9992de3e90191de369270ea4b6c1b171b7941743614cc50822ddc1fe654 \
    RUBYGEMS_VERSION=2.6.12 \
    BUNDLER_VERSION=1.15.3

RUN env
Sign up to request clarification or add additional context in comments.

7 Comments

You can also combine this with quotes and with expansion of existing environment variables. For example, it is common to want to expand the path as part of a set of environment variables: ENV PATH="$PATH:/app" \ APPVAR="foo"
Weirdly enough, I tried to use the first var in the second var, and it just resolved to an empty string, no matter if it's one line or multi-line
@milosmns Yes I noticed that behavior too
That would need build arguments
@milosmns @kiran-challa To expound on Tarun's comment, any variable declared on a given ENV command will not be set until the command has completed, so that behavior is as expected (try echo $V_NOT_SET on any *nix based system). Enter build arguments. As a side note, export V_NOT_SET="hello" echo $V_NOT_SET will also result in only whitespace; adding an export before that doesn't help; inserting && (a.k.a. AND_IF) between the export and the echo will result in hello being echoed, because it now requires the first command to finish before evaluating the second.
|
39

You do not need to worry about many ENV commands each creating a new intermediate layer for your final image created by your Dockerfile.

from Best practices for writing Dockerfiles

Minimize the number of layers

Prior to Docker 17.05, and even more, prior to Docker 1.10, it was important to minimize the number of layers in your image. The following improvements have mitigated this need:

  • In Docker 1.10 and higher, only RUN, COPY, and ADD instructions create layers. Other instructions create temporary intermediate images, and no longer directly increase the size of the build.

  • Docker 17.05 and higher add support for multi-stage builds, which allow you to copy only the artifacts you need into the final image. This allows you to include tools and debug information in your intermediate build stages without increasing the size of the final image.

7 Comments

@Tomanow It's a direct quote from the docker docs (see link), but I think the confusion is that the layers created by ENV are "temporary", which I assume means squashed into a different layer at some point, but that is just a guess as to implementation.
From my testing with docker 19 ENV lines do not create layers in the image, when building the image it says "Removing intermediate container", then inspecting the layers afterwards with dive, I can see there are no layers created for ENV.
While it's great that the ENV layers get squashed, they still have to be created in the first place, which can take awhile on my laptop (is this unusual?), so I think it is better to go the multiline route anyway to save on build time.
According to the article linked (Best practices...): "Each ENV line creates a new intermediate layer, just like RUN commands. "
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.