Should You Try to Set Environment Variables Based on a Command Output While Building a Docker Image?

Ever wanted to have dynamically-set environment variables when writing a new Dockerfile for building your image? Not dynamically-set before you start building the image (that’s a job for ARG and –build-arg), but set during the image build process, while the Dockerfile lines are interpreted, not bound to the host machine in any way.

Re-executing the command in question several times will either be wasteful, or plain wrong. Something like a timestamp:

(Dockerfile snippet:)

RUN export VARIABLE=$(date)
# or
RUN export VARIABLE=`date`

This might be counterintuitive, when thinking of Dockerfiles as a kind-of setup script. It is, but not quite. Let’s take a look at what’s going on when a Dockerfile is used to build a Docker image.

Each line, is executed in a fresh container. The resulting container state, after the line has been interpreted, is saved in a temporary image and used to start a container for the next command. Think of an image as a powered-down computer, which did not save any state apart from the files on disk (and maybe Docker-specific properties like EXPOSE, which are like image settings).

When you set an environment variable like above, and shut down the machine, it is not saved. A new container starts, sets an environment variable, an image is saved from the container and the new VARIABLE is not in it. The container is destroyed. Poof.

How to work around that?

A simple way to fix this, is using a temporary file inside the image and writing the value into it. This way, it is preserved for future commands, if you don’t forget to set it.

# write the output of the date command into a file called tmp_variable
RUN date > /root/tmp_variable
# set it right before running a command which needs it
RUN VARIABLE=$(cat /root/tmp_variable); some_command_which_relies_on_it.sh

# let's assume the variable is kinda secret, and you want it to go away
# you might do something like:
RUN rm /root/tmp_variable

# DANGER: even if you remove the file, it will be in a *layer* of the image
# so it's not completely gone, even though the container will not see it.
You probably don't want to use this approach if the image will be made public

If you need the command running in containers started from the final image to have access to the value of this variable as well, you should make use of an entrypoint script. The script is usually simply a bash script. In this case it sets the environment variable from inside the container and then runs the command, passing any arguments transparently.

If you have the time, you should take a few minutes to really understand the different kinds of variables when working with Docker, what they are good for and how you can use them.