Using variable interpolation in string in Docker

I am having trouble creating and using variables in a Dockerfile – I build a Docker image via a Dockerfile with this command:

$ docker build --build-arg s=scripts/a.sh -t a .

(So because I use –build-arg, $s will be an available argument in the Dockerfile, and this part works)

  • Updating docker registry in ubuntu 14.0
  • Docker: how to fix “Layer already being pulled by another client. Waiting”
  • Modifying 201-vmss-ubuntu-autoscale Azure quickstart template to include Docker extension
  • Apache2 in docker container gives 403 on statically served files
  • Docker : Install Maven dependencies during build stage only?
  • Docker do not work from build remotely at Jenkins slave. What' the reason?
  • The Dockerfile is like so:

    ARG s
    
    RUN echo $s 
    
    RUN useradd -ms /bin/bash newuser
    
    USER newuser
    WORKDIR /home/newuser
    
    ENV fn=$(filename $s)  # fails on this line
    
    COPY $s .
    
    ENTRYPOINT ["/bin/bash", "/home/newuser/$fn"]
    

    The problem I have is that the Docker build is failing on the line indicated above.

    Error response from daemon: Syntax error - can't find = in "$s)". Must be of the form: name=value
    

    If I change that line to this:

    RUN fn=$(filename $s)
    

    I get this error:

    Error: Command failed: docker build --build-arg s=scripts/a.sh -t a .
    The command '/bin/sh -c fn=$(filename $s)' returned a non-zero code: 127
    

    Anyone know the correct way to

    1. Create a variable inside the docker file
    2. Use string interpolation with that variable so that I can reference the variable in the ENTRYPOINT arguments like so:

      ENTRYPOINT [“/bin/bash”, “/home/newuser/$var”]

    Even if I do this:

    ARG s
    
    ARG sname
    
    RUN echo $s          # works as expected
    RUN echo $sname      # works as expected
    
    RUN useradd -ms /bin/bash newuser
    
    USER newuser
    WORKDIR /home/newuser
    
    
    COPY $s .  # works as expected (I believe)
    
    ENTRYPOINT /bin/bash /home/newuser/$sname  # does not work as expected
    

    even though I am using the “non-JSON” version of ENTRYPOINT, it still doesn’t seem to pick up the value for the $sname variable.

  • Library for Docker Remote API v1.24 for Go?
  • Port mapping in Docker
  • run docker commands in non-interactive shell
  • Docker + WerckerCi: Tag not found
  • Kubernetes 1.2.2: api-server fails: can't find mounted certs for TLS on etcd
  • Dockerizing an SSH Daemon Service using my own key
  • 2 Solutions collect form web for “Using variable interpolation in string in Docker”

    I would avoid using variable in ENTRYPOINT at all. It’s tricky and requires a deep understanding of what is going on. And is easy to break it by accident. Just consider one of the following.

    Create link with the known name to your start script.

    RUN ln -s /home/newuser/$sname /home/newuser/docker_entrypoint.sh
    ENTRYPOINT ["/home/newuser/docker_entrypoint.sh"]
    

    or write standalone entrypoint script that runs what you need.

    But if you want to know how and why solutions in your questions work just keep reading.

    First some definitions.

    • ENV – is environment variable available during buildtime (docker build) and runtime (docker run)
    • ARG – is environment variable available only during buildtime

    If you look at https://docs.docker.com/engine/reference/builder/#environment-replacement you see the list of dockerfile instructions that support those environment variables directly. This is why COPY “picks up the variable” as you said.

    Please note that there is no RUN nor ENTRYPOINT. How does it work?

    You need to dig into the documentation. First RUN (https://docs.docker.com/engine/reference/builder/#run). There are 2 forms. The first one executes command through the shell and this shell has access to buildtime environment variables.

    # this works because it is called as /bin/sh -c 'echo $sname'
    # the /bin/sh replace $sname for you      
    RUN echo $sname 
    
    # this does NOT work. There is no shell process to do $sname replacement 
    # for you
    RUN ["echo", "$sname"]
    

    Same thing applies to the ENTRYPOINT and CMD except only runtime variables are available during container start.

    # first you need to make some runtime variable from builtime one
    ENV sname $sname
    
    # Then you can use it during container runtime
    # docker runs `/bin/sh -c '/bin/bash /home/newuser/$sname'` for you
    # and this `/bin/sh` proces interprets `$sname`
    ENTRYPOINT /bin/bash /home/newuser/$sname
    
    # but this does NOT work. There is no process to interpolate `$sname`
    # docker runs what you describe.
    ENTRYPOINT ["/bin/bash", "/home/newuser/$sname"]
    

    edit 2017-04-03: updated links to the docker documentations and slight rewording to avoid confusion that I sense from other answers and comments.

    I requested @Villem to answer, and his answer is much more definitive, but the following will work (just is not a stable solution). His answer is basically saying that this answer is not a good way to do it:

    ARG s           # passed in via --build-arg s=foo
    ARG sname       # passed in via --build-arg sname=bar
    
    RUN echo $s       
    RUN echo $sname  
    
    ENV sname $sname   # this is the key part
    
    RUN useradd -ms /bin/bash newuser
    
    USER newuser
    WORKDIR /home/newuser
    
    COPY $s .
    
    ENTRYPOINT /bin/bash /home/newuser/$sname   # works, but is not stable!
    

    don’t ask me why the COPY command picks up the variable that was declared via ARG, but that the ENTRYPOINT command does not seem to pick up the variable declared via ARG, but only picks up the variable declared via ENV. At least, this appears to be the case.

    Docker will be the best open platform for developers and sysadmins to build, ship, and run distributed applications.