Each Dockerfile RUN
step runs a new container and a new shell. If you try to set an environment variable in one shell, it will not be visible later on. For example, you might experiment with this Dockerfile:
FROM busybox
ENV FOO=foo1
RUN export FOO=foo2
RUN export BAR=bar
CMD echo FOO is $FOO, BAR is $BAR
# Prints "FOO is foo1, BAR is "
There are three good solutions to this. In order from easiest/best to hardest/most complex:
-
Avoid needing the environment variables at all. Install software into “system” locations like
/usr
; it will be isolated inside the Docker image anyways. (Don’t use an additional isolation tool like Python virtual environments, or a version manager likenvm
orrvm
; just install the specific thing you need.) -
Use
ENV
. This will work:FROM busybox ENV FOO=foo2 ENV BAR=bar CMD echo FOO is $FOO, BAR is $BAR # Prints "FOO is foo2, BAR is bar"
-
Use an entrypoint script. This typically looks like:
#!/bin/sh # Read in the file of environment settings . /opt/wherever/env # Then run the CMD exec "$@"
COPY
this script into your Dockerfile. Make it be theENTRYPOINT
; make theCMD
be the thing you’re actually running.FROM busybox WORKDIR /app COPY entrypoint.sh . COPY more_stuff . ENTRYPOINT ["/app/entrypoint.sh"] CMD ["/app/more_stuff/my_app"]
If you care about such things, environment variables you set via this approach won’t be visible in
docker inspect
or adocker exec
debug shell; but if youdocker run -it ... sh
they will be visible. This is a useful and important enough pattern that I almost always useCMD
in my Dockerfiles unless I’m specifically trying to do first-time setup like this.