Search code examples
postgresqldockerdockerfile

Dockerfile RUN command cannot use heredoc with build args?


When I set an env var and run:

#! /bin/sh
cat << EOF > foo.txt
this is ${TEMPVAR} and maybe this is too? $TEMPVAR
and that's all folks!
EOF

It generates foo.txt with the right values.

But when I try to use a similar method for dockerfile RUN command, it doesn't work?

FROM --platform=linux/amd64 postgres:16-alpine AS base

RUN cat << EOSQL > /docker-entrypoint-initdb.d/start.sql \
  create role ${ROLE1} with login password '${ROLE1PWD}'; \
  create role ${ROLE2} with login password '${ROLE2PWD}'; \
  EOSQL

When the image is being built, it fails, with the error:

1.188 cat: can't open 'create': No such file or directory                                                                                
1.188 cat: can't open 'role': No such file or directory                                                                                  
1.188 cat: can't open 'with': No such file or directory                                                                                  
1.188 cat: can't open 'login': No such file or directory
1.188 cat: can't open 'password': No such file or directory
1.188 cat: can't open '${ROLE1PWD}': No such file or directory
1.207 /bin/sh: create: not found

What's wrong with the RUN command? Dockerfile is being invoked by docker compose which picks up .env values automatically so not sure what else to try?

Update.

The suggested questions are irrelevant because I'm no longer struggling to pass build args to the dockerfile. I'm struggling to understand why heredocs don't work in dockerfile. I've evolved the code to:

RUN /bin/bash -c "cat <<-EOSQL > /docker-entrypoint-initdb.d/start.sql \
  create role ${ROLE1} with login password '${ROLE1PWD}'; \
  create role ${ROLE2} with login password '${ROLE2PWD}'; \
EOSQL\
"

... and edited with vi to circumvent the docker formatter constantly indenting the EOSQL. I also added an extra line at the end. Still no joy.


Solution

  • This...

    RUN cat << EOSQL > /docker-entrypoint-initdb.d/start.sql \
      create role ${ROLE1} with login password '${ROLE1PWD}'; \
      create role ${ROLE2} with login password '${ROLE2PWD}'; \
      EOSQL
    

    Isn't going to work because it's delivered to the shell as a single line (the \ at the end of each line means "turn this into a single virtual line"). Here documents only work when there are actual line breaks in your script, and there's no easy way to get that with this form of a RUN statement.

    Why not just put your script in a file and COPY it into the image?

    COPY start.sql /docker-entrypoint-initdb.d/start.sql
    

    With recent versions of Docker (where "recent" actually seems to mean post-2021) there is actually support for heredocs in the Dockerfile, so you can write:

    COPY <<EOSQL /docker-entrypoint-initdb.d/start.sql
    create role ${ROLE1} with login password '${ROLE1PWD}';
    create role ${ROLE2} with login password '${ROLE2PWD}';
    EOSQL