How to assign variable and use sed to replace contents of configuration file in Dockerfile?

This works for me:

FROM alpine

COPY ipaddr /ipaddr
COPY ipconf /ipconf
RUN export IPADDR=$(cat /ipaddr) ; sed -i -r "s/IPADDR/${IPADDR}/g" /ipconf
CMD cat /ipconf

With ipconf & ipaddr

IP address is IPADDR

1.2.3.4

Then:

>docker build -t dynchange .
Sending build context to Docker daemon   5.12kB
Step 1/5 : FROM alpine
---> 3fd9065eaf02
Step 2/5 : COPY ipaddr /ipaddr
---> Using cache
---> 1828c3157d41
Step 3/5 : COPY ipconf /ipconf
---> 4f43e88e4425
Step 4/5 : RUN export IPADDR=$(cat /ipaddr) ;  sed -i -r "s/IPADDR/${IPADDR}/g" /ipconf
---> Running in 722c3d3d9d9e
Removing intermediate container 722c3d3d9d9e
---> 32e31198b70a
Step 5/5 : CMD cat /ipconf
---> Running in 72ea59543a7f
Removing intermediate container 72ea59543a7f
---> 73f8a50d650a
Successfully built 73f8a50d650a
Successfully tagged dynchange:latest

>docker run --rm dynchange
IP address is 1.2.3.4

However, this is exactly how I would not do it. A better way is to use a build arg:

FROM alpine

COPY ipconf /ipconf
ARG IPADDR
RUN sed -i -r "s/IPADDR/${IPADDR}/g" /ipconf
CMD cat /ipconf

Then:

>docker build -t dynchange --build-arg IPADDR=4.5.6.7  .
Sending build context to Docker daemon   5.12kB
Step 1/5 : FROM alpine
---> 3fd9065eaf02
Step 2/5 : COPY ipconf /ipconf
---> Using cache
---> f5ac88ee48d5
Step 3/5 : ARG IPADDR
---> Running in 58d972e14660
Removing intermediate container 58d972e14660
---> fc3de48160c6
Step 4/5 : RUN sed -i -r "s/IPADDR/${IPADDR}/g" /ipconf
---> Running in 8ec855697510
Removing intermediate container 8ec855697510
---> 67e946559316
Step 5/5 : CMD cat /ipconf
---> Running in a5260c593c02
Removing intermediate container a5260c593c02
---> 567a31a517d1
Successfully built 567a31a517d1
Successfully tagged dynchange:latest

>docker run --rm dynchange
IP address is 4.5.6.7

However I'm not even sure this is the right solution. I would either:

  • build the configuration file outside of the container, and create/replace it with a COPY. This gives full control on its contents.
  • pass that IP address as a parameter when the container is run (environment variable or else), so you have one single container image that can be used in several environments. The CMD orENTRYPOINT can be script that patches the file before calling the main command. You can also pass the configuration file as a VOLUME (this can avoid issues if CMD is run as an application user and patching the config requires root privileges).

Thanks all. I the matter is on delimiter. I change "/" to be ":" and get it worked. I post the result in here. In "Step 65/78", I print the script content and "Step 67/78" run the script well. Thanks @xenoid and @Kusalananda.

Step 63/78 : RUN echo "#!/bin/sh -x\nsed -i 's:PRIVATEIP:'"$(cat /home/ip_variable)"':g' /etc/fail2ban/jail.local\nsed -i 's:PRIVATEIP:'"$(cat /home/ip_variable)"':g' /etc/opendkim/TrustedHosts" > /home/ip_change_script.sh
 ---> Running in 251d6741c37e
Removing intermediate container 251d6741c37e
 ---> 95c4aa62d74a
Step 64/78 : RUN cat /home/ip_variable
 ---> Running in 8825ef517fb8
172.17.0.2/16
Removing intermediate container 8825ef517fb8
 ---> 39672eb7021d
Step 65/78 : RUN cat /home/ip_change_script.sh
 ---> Running in 2161051ee4cf
#!/bin/sh -x
sed -i 's:PRIVATEIP:'172.17.0.2/16':g' /etc/fail2ban/jail.local
sed -i 's:PRIVATEIP:'172.17.0.2/16':g' /etc/opendkim/TrustedHosts
Removing intermediate container 2161051ee4cf
 ---> 407ccc7106f7
Step 66/78 : RUN chmod 755 /home/ip_change_script.sh
 ---> Running in 0f6ca7f78ba8
Removing intermediate container 0f6ca7f78ba8
 ---> 250afb327db0
Step 67/78 : RUN sh /home/ip_change_script.sh
 ---> Running in 4b209e18854b
Removing intermediate container 4b209e18854b
 ---> d45d024e1359
Step 68/78 : RUN touch /home/service_boot.log
 ---> Running in 3548dc45f466
Removing intermediate container 3548dc45f466
 ---> 587527e18ab2
Step 69/78 : RUN cat /home/service_boot.log
 ---> Running in b068b00cd584
Removing intermediate container b068b00cd584
 ---> 3183e6b281ed
Step 70/78 : COPY boot_service.sh /home/boot_service.sh
 ---> 9a73dd4fd87d
Step 71/78 : RUN chmod 755 /home/boot_service.sh
 ---> Running in 2f226574e28d
Removing intermediate container 2f226574e28d
 ---> 8a0c14218e1b
Step 72/78 : RUN cat /home/boot_service.sh
 ---> Running in 5fa64827b6dd

An assignment does not produce output and it's therefore pointless to pipe it to another process.

Instead, use ; to divide the pipeline inte two sequential commands:

IP=$(cat /root/ip_variable); sed -i "s/PRIVATEIP/$IP/g" /etc/fail2ban/jail.local

I'm unfamiliar with Docker and it's RUN command. If it only takes a single command, then use

RUN sh -c 'IP=$(cat /root/ip_variable); sed -i "s/PRIVATEIP/$IP/g" /etc/fail2ban/jail.local'

or

RUN sed -i "s/PRIVATEIP/$(cat /root/ip_variable)/g" /etc/fail2ban/jail.local