Using the RUN instruction in a Dockerfile with 'source' does not work
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
When working with Dockerfiles to automate the deployment of applications via containerization, you might encounter limitations or unexpected behavior with certain shell commands. One such challenge arises with using the RUN instruction, especially when attempting to leverage the source command inside a Dockerfile. This article delves into why using source with RUN may not yield the expected results, provides technical explanations, and offers best practices for achieving the desired outcomes.
Technical Explanations
Understanding RUN Instruction
In a Dockerfile, the RUN instruction executes commands in a new layer on top of the current image and commits the results. The resulting image is then used for the next step in the Dockerfile. A typical RUN command in a Dockerfile looks like this:
Each RUN creates a temporary shell environment to execute the specified command. It's crucial to understand that RUN spawns each command in its own shell, isolated from previous and subsequent commands. This isolation is the central reason why the source command appears to behave unexpectedly.
Nature of the source Command
The source command is a feature of Unix-like operating system shells, like bash, used to execute commands from a file in the current shell environment. This means it can modify the environment by setting variables, changing directories, etc., which persist after the source command is finished.
When you try to use source in a Dockerfile within a RUN instruction, like this:
you might notice that any environment changes (like setting or exporting variables) don't persist beyond the execution of that RUN command. This happens because Docker spawns a new shell for each RUN command, and these shells do not share environment changes.
Why source Doesn't Persist in Docker RUN
The isolation between different RUN commands is by design. Docker creates new layers for each command and the changes made within a layer do not propagate to the outer environment unless explicitly saved in the filesystem. Since source only affects the shell environment it runs in, and because the environment is not saved as part of the Docker image layer, any changes are lost once the RUN command completes.
Solutions and Alternatives
Using Environment Files
Instead of source, you can leverage files that Docker will load at container startup:
And in your service container, you can specify the env_file in your docker-compose.yml (if applicable), or set environment variables during build time through an ARG or ENV instruction:
Using ARG and ENV
To pass environment variables during the build process, use the ARG instruction and define default values:
You can override this argument during build:
Inline Commands
For setting environment variables or configurations that must occur at build time, use a single RUN layer with multiple commands:
Or use a shell script:
Table: Key Concepts
| Command/Instruction | Purpose | Persistence | Additional Notes |
RUN | Execute commands during build | No persistence of shell environment | Each RUN in a new container layer |
source | Execute script in the current shell | Temporary, not persisted in Docker | Use eval for alternatives |
ARG | Build-time variables | Passed during build, but not environment | Use ENV to persist as environment variables |
ENV | Set environment variables for a container | Persisted in container environment | Accessible from running container |
Final Thoughts
Understanding Docker's build environment is crucial for efficiently working with Dockerfiles. The design of Docker's image layers necessitates alternative strategies to manage persistent configurations. Command chaining, leveraging environment files, or using Docker's built-in argument and environment variable handling mechanisms are effective approaches to address the limitations encountered with the RUN instruction and the source command. Debugging any issue often begins with a rich understanding of how environments behave inside Docker, guiding you toward effective solutions.

