126

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?

BrianWilson
  • 1,653
  • 2
  • 12
  • 16
  • 1
    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}` – Jan Matějka Apr 06 '22 at 15:01

2 Answers2

221

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
Dr Rob Lang
  • 6,659
  • 5
  • 40
  • 60
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • 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"` – JeremyDouglass Aug 07 '18 at 19:10
  • 5
    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 Apr 19 '19 at 20:47
  • @milosmns Yes I noticed that behavior too – Kiran Jul 29 '20 at 21:57
  • 1
    That would need build arguments – Tarun Lalwani Jul 30 '20 at 04:30
  • 3
    @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. – hlongmore Sep 11 '20 at 03:23
  • What if you're injecting values from ARGS and the ARG values contain spaces? Should we wrap stuff in quotes or something by default? – Triynko Oct 28 '21 at 17:11
  • If you use `ENV X=$ARG_X`, docker will take care of the spaces on its own – Tarun Lalwani Oct 28 '21 at 17:46
36

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.

Pang
  • 9,564
  • 146
  • 81
  • 122
Mike Lippert
  • 2,101
  • 1
  • 18
  • 12
  • This is incorrect as per: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#env – Tomanow Oct 29 '18 at 14:30
  • @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. – Mike Lippert Oct 29 '18 at 17:15
  • 5
    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`. – Stephen Paulger Sep 04 '19 at 08:35
  • 14
    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. – Chinoto Vokro Mar 17 '20 at 04:38
  • What @ChinotoVokro said.... The layers do get created which then destroyed... but nevertheless it is a pain. – Jonathan Alfaro Aug 26 '20 at 06:20
  • 3
    According to the article linked (Best practices...): "Each ENV line creates a new intermediate layer, just like RUN commands. " – Nick Oct 02 '20 at 20:55
  • @Nick "Other instructions create temporary intermediate images". So you're not wrong on that, but it does not create a layer that contribute to the size. – Leo Lei Mar 10 '22 at 12:18