skip to Main Content

This is my Dockerfile (I need to generate the path in runtime, I don’t know it upfront):

FROM ubuntu:22.04
RUN year=$(date +%Y) 
  && echo "export PATH=${PATH}:/usr/local/${year}" >> /root/.bashrc

Then, I build the image and run a container:

$ docker build . -t tmp
$ docker run --rm tmp env

The PATH doesn’t include /usr/local/2024.

AFAIU, it’s not possible to use ENV for this, since the PATH is not static, but generated when the image is being built.

2

Answers


  1. Firstly, understand that Docker’s ENV instruction doesn’t support shell command execution directly. So, you can’t use $(date +%Y) directly within an ENV instruction to dynamically generate part of your path. However, you can cleverly work around this limitation using a multi-stage build process. Here’s how you can do it effectively:

    Multi-Stage Build Approach: Utilize a two-stage build process. In the first stage, generate the dynamic part of your PATH and output it to a file. In the second stage, copy this file over and use it to set the PATH environment variable correctly.

    Copy code
    # Stage 1: Generate the dynamic PATH component
    FROM ubuntu:22.04 AS builder
    RUN echo "PATH=/usr/local/$(date +%Y):${PATH}" > /path.env
    
    # Stage 2: Build the final image and set the PATH
    FROM ubuntu:22.04
    COPY --from=builder /path.env /etc/environment
    # Ensure the PATH is sourced for both interactive and non-interactive shells
    RUN echo 'source /etc/environment' >> /etc/bash.bashrc && 
        echo 'source /etc/environment' >> /etc/profile
    

    This approach ensures that your dynamic PATH is respected in both interactive and non-interactive shell sessions, aligning with Docker’s best practices for setting environment variables that need to be evaluated or generated at build time.

    Login or Signup to reply.
  2. When you run a non-interactive shell, bash doesn’t read your .bashrc file. Compare:

    $ docker run --rm pathtest env | grep PATH
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    

    With:

    $ docker run -it --rm pathtest
    root@6e9bfd8a4e04:/# env | grep PATH
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/2024
    

    Note that in the second case, in which we start an interactive shell, PATH includes /usr/local/2024.


    I think the best way of handling this would be to set PATH in an ENTRYPOINT script. E.g.:

    FROM ubuntu:22.04
    
    ENTRYPOINT ["sh", "-c", "export PATH="${PATH}:/usr/local/$(date +%Y)" && exec "$@"", "--"]
    CMD ["bash"]
    

    This will set PATH regardless of what you pass as the command, so your test now works as expected:

    $ docker run --rm pathtest env | grep PATH
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/2024
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search