221

Is it possible to set a docker ENV variable to the result of a command? Like:

ENV MY_VAR whoami 

i want MY_VAR to get the value "root" or whatever whoami returns

2

9 Answers 9

46

As an addition to DarkSideF answer.

You should be aware that each line/command in Dockerfile is ran in another container.

You can do something like this:

RUN export bleah=$(hostname -f);echo $bleah; 

This is run in a single container.

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

1 Comment

Just to clarify - $bleah is not available anywhere outside this RUN command, not even on the next line in the same dockerfile, let alone in another image it's based off. Really obvious missing feature from docker here, it does look like writing to and reading from a file is the only way to actually store (dynamic) variables in images and pass them between images, which seems super hacky.
41

At this time, a command result can be used with RUN export, but cannot be assigned to an ENV variable.

Known issue: https://github.com/docker/docker/issues/29110

Comments

33

If you run commands using sh as it seems to be the default in docker.

You can do something like this:

RUN echo "export VAR=`command`" >> /envfile RUN . /envfile; echo $VAR .... RUN . /envfile; echo $VAR # can be re-used .... RUN rm /envfile # Remove the file at the end if it contains secrets so that they are not persisted in the container image 

IMPORTANT: if secrets are stored in that file, delete it before finishing the container image.

This way, you build a env file by redirecting output to the env file of your choice. It's more explicit than having to define profiles and so on.

Then as the file will be available to other layers, it will be possible to source it and use the variables being exported. The way you create the env file isn't important.

Then when you're done you could remove the file to make it unavailable to the running container.

The . is how the env file is loaded.

6 Comments

This always comes handy!
this answer is underrated.
This results in a permissions issue, how do you fix it?
@thus__ try adding #!/bin/sh at the top of your envfile, and ["chmod", "+x", "/envfile"] to your Dockerfile
@thus__ You probably mistyped something if you get a permission error. @JohnF's comment would help if you forgot the . after RUN but then it will fail for other reasons subsequently (i.e. the variable will not be set after the script ends).
|
26

I had same issue and found way to set environment variable as result of function by using RUN command in dockerfile.

For example i need to set SECRET_KEY_BASE for Rails app just once without changing as would when i run:

docker run -e SECRET_KEY_BASE="$(openssl rand -hex 64)" 

Instead it i write to Dockerfile string like:

RUN bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" >> /etc/bash.bashrc' 

and my env variable available from root, even after bash login. or may be

RUN /bin/bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" > /etc/profile.d/docker_init.sh' 

then it variable available in CMD and ENTRYPOINT commands

Docker cache it as layer and change only if you change some strings before it.

You also can try different ways to set environment variable.

2 Comments

Is /etc/profile.d/docker_init.sh still a thing? This answer is the only mention of it that I can find with Google, and it doesn't work for me. Was it perhaps part of a Docker execution engine in 2016 that is no longer current?
@SigmaX It's not a docker thing, it's more a Linux thing. Any *.sh file inside /etc/profile.d/ is used to populate the environment
19

This answer is a response to @DarkSideF,

The method he is proposing is the following, in Dockerfile :

RUN bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" >> /etc/bash.bashrc' 

( adding an export in the /etc/bash.bashrc)

It is good but the environment variable will only be available for the process /bin/bash, and if you try to run your docker application for example a Node.js application, /etc/bash.bashrc will completely be ignored and your application won't have a single clue what SECRET_KEY_BASE is when trying to access process.env.SECRET_KEY_BASE.

That is the reason why ENV keyword is what everyone is trying to use with a dynamic command because every time you run your container or use an exec command, Docker will check ENV and pipe every value in the process currently run (similar to -e).

One solution is to use a wrapper (credit to @duglin in this github issue). Have a wrapper file (e.g. envwrapper) in your project root containing :

#!/bin/bash export SECRET_KEY_BASE="$(openssl rand -hex 64)" export ANOTHER_ENV "hello world" $* 

and then in your Dockerfile :

... COPY . . RUN mv envwrapper /bin/. RUN chmod 755 /bin/envwrapper CMD envwrapper myapp 

Comments

10

As an addition to @DarkSideF's answer, if you want to reuse the result of a previous command in your Dockerfile during in the build process, you can use the following workaround:

  1. run a command, store the result in a file
  2. use command substitution to get the previous result from that file into another command

For example :

RUN echo "bla" > ./result RUN echo $(cat ./result) 

For something cleaner, you can use also the following gist which provides a small CLI called envstore.py :

RUN envstore.py set MY_VAR bla RUN echo $(envstore.py get MY_VAR) 

Or you can use python-dotenv library which has a similar CLI.

1 Comment

I am having difficulty with this interacting with ENV. Works with RUN though.
7

Not sure if this is what you were looking for, but in order to inject ENV vars or ARGS into your .Dockerfile build this pattern works.

in your my_build.sh:

echo getting version of osbase image to build from OSBASE=$(grep "osbase_version" .version | sed 's/^.*: //') echo building docker docker build -f \ --build-arg ARTIFACT_TAG=$OSBASE \ PATH_TO_MY.Dockerfile \ -t my_artifact_home_url/bucketname:$TAG . 

for getting an ARG in your .Dockerfile the snippet might look like this:

FROM scratch ARG ARTIFACT_TAG FROM my_artifact_home_url/bucketname:${ARTIFACT_TAG} 

alternatively for getting an ENV in your .Dockerfile the snippet might look like this:

FROM someimage:latest ARG ARTIFACT_TAG ENV ARTIFACT_TAG=${ARTIFACT_TAG} 

the idea is you run the shell script and that calls the .Dockerfile with the args passed in as options on the build.

Comments

2
ARG KUBERNETES_VERSION="v1.25.2" ENV ARCH_CMD='eval uname -m | grep -q x86_64 && echo "amd64" || echo "arm64"' RUN curl -k -L "https://dl.k8s.io/release/$KUBERNETES_VERSION/bin/linux/$($ARCH_CMD)/kubectl" -o /usr/local/bin/kubectl \ && chmod +x /usr/local/bin/kubectl 

basically store the command along with eval in an environment variable for later easy access but the result of the command itself is not saved as a

Comments

1

From an openSUSE Leap 15 docker image, then maybe to adapt accordingly:

# Put variable with value in /etc/environment RUN echo "MY_VAR=$(whoami)" >> /etc/environment # And source /etc/environment in default .bashrc (it seems /etc/environment is not "automatically" sourced, or it is at beginning of docker build then adding afterward does not work ?) RUN echo "source /etc/environment" >> /etc/bash.bashrc 

Edit: as mentioned by @tripleee this works only when subsequently running interactive Bash sessions (or you separately source /etc/environment when you need to).

If you want to run a specific command as ENTRYPOINT you can put in in a script that sources /etc/environment before doing anything, like for example just for echo "MY_VAR = ${MY_VAR}" as ENTRYPOINT:

Run.sh:

#!/bin/bash source /etc/environment echo "MY_VAR = ${MY_VAR}" 

Dockerfile:

FROM opensuse/leap:15.0 # Only need to put variable with value in /etc/environment RUN echo "MY_VAR=$(whoami)" >> /etc/environment COPY Run.sh / ENTRYPOINT [ "/Run.sh" ] 

4 Comments

This only works if you subsequently run interactive Bash commands.
Ah, yes indeed, this works because my entry point is bash.
Well, it works in your ENTRYPOINT because you are explicitly sourcing /etc/environment in that file. My point is that this script (and most other scripts you run noninteractively) will not read /etc/bash.bashrc because it is only sourced by interactive shells (bash -i)
Yeah I understand, but since docker works as it works we need to adapt to have it almost work like original system, even if its not the best I tend to always run a script as an entry point so that I do what I want and can do things such as sourcing /etc/environment, doing mount -a when I have stuff I want to be mounted from fstab, other stuff that are not done like at standard boot/login since this is not how docker works. Even to have systemctl managing service, I fortunately found docker-systemctl-replacement that does the job, see stackoverflow.com/a/63642719/11159476

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.