I'm experimenting with new HEREDOC syntax in Dockerfile. There is example in official docs that I assume is running some heredoc as script in bash:

# syntax=docker/dockerfile:1FROM debianRUN <<EOT bashapt-get updateapt-get install -y vimEOT

when I build image like that it seems to work

$ docker buildx build --file ./.docker/Dockerfile-test --no-cache --progress=plain --secret id=composer-auth,src=$(pwd)/auth.json --tag heredoc:test .#1 [internal] load build definition from Dockerfile-test#1 transferring dockerfile: 34B#1 transferring dockerfile: 36B done#1 DONE 0.0s......#8 [1/2] FROM docker.io/library/debian@sha256:534da5794e770279c889daa891f46f5a530b0c5de8bfbc5e40394a0164d9fa87#8 CACHED#9 [2/2] RUN <<EOT bash#9 0.529 Get:1 http://deb.debian.org/debian bullseye InRelease [116 kB]#9 0.885 Get:2 http://deb.debian.org/debian-security bullseye-security InRelease [48.4 kB]#9 1.193 Get:3 http://deb.debian.org/debian bullseye-updates InRelease [44.1 kB]#9 1.294 Get:4 http://deb.debian.org/debian bullseye/main amd64 Packages [8183 kB]#9 2.110 Get:5 http://deb.debian.org/debian-security bullseye-security/main amd64 Packages [218 kB]#9 2.134 Get:6 http://deb.debian.org/debian bullseye-updates/main amd64 Packages [14.6 kB]#9 2.940 Fetched 8624 kB in 3s (3227 kB/s)#9 2.940 Reading package lists...#9 3.379 Reading package lists...#9 3.768 Building dependency tree...#9 3.881 Reading state information...#9 4.059 The following additional packages will be installed:#9 4.059 libgpm2 vim-common vim-runtime xxd#9 4.061 Suggested packages:#9 4.061 gpm ctags vim-doc vim-scripts#9 4.149 The following NEW packages will be installed:#9 4.149 libgpm2 vim vim-common vim-runtime xxd#9 4.224 0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.#9 4.224 Need to get 8174 kB of archives.#9 4.224 After this operation, 36.9 MB of additional disk space will be used.#9 4.224 Get:1 http://deb.debian.org/debian bullseye/main amd64 xxd amd64 2:8.2.2434-3+deb11u1 [192 kB]#9 4.307 Get:2 http://deb.debian.org/debian bullseye/main amd64 vim-common all 2:8.2.2434-3+deb11u1 [226 kB]#9 4.329 Get:3 http://deb.debian.org/debian bullseye/main amd64 libgpm2 amd64 1.20.7-8 [35.6 kB]#9 4.330 Get:4 http://deb.debian.org/debian bullseye/main amd64 vim-runtime all 2:8.2.2434-3+deb11u1 [6226 kB]#9 4.583 Get:5 http://deb.debian.org/debian bullseye/main amd64 vim amd64 2:8.2.2434-3+deb11u1 [1494 kB]#9 4.784 debconf: delaying package configuration, since apt-utils is not installed#9 4.822 Fetched 8174 kB in 0s (16.7 MB/s)#9 4.838 Selecting previously unselected package xxd.(Reading database ... 6661 files and directories currently installed.)#9 4.848 Preparing to unpack .../xxd_2%3a8.2.2434-3+deb11u1_amd64.deb ...#9 4.850 Unpacking xxd (2:8.2.2434-3+deb11u1) ...#9 4.885 Selecting previously unselected package vim-common.#9 4.886 Preparing to unpack .../vim-common_2%3a8.2.2434-3+deb11u1_all.deb ...#9 4.892 Unpacking vim-common (2:8.2.2434-3+deb11u1) ...#9 4.922 Selecting previously unselected package libgpm2:amd64.#9 4.923 Preparing to unpack .../libgpm2_1.20.7-8_amd64.deb ...#9 4.926 Unpacking libgpm2:amd64 (1.20.7-8) ...#9 4.948 Selecting previously unselected package vim-runtime.#9 4.949 Preparing to unpack .../vim-runtime_2%3a8.2.2434-3+deb11u1_all.deb ...#9 4.953 Adding 'diversion of /usr/share/vim/vim82/doc/help.txt to /usr/share/vim/vim82/doc/help.txt.vim-tiny by vim-runtime'#9 4.957 Adding 'diversion of /usr/share/vim/vim82/doc/tags to /usr/share/vim/vim82/doc/tags.vim-tiny by vim-runtime'#9 4.958 Unpacking vim-runtime (2:8.2.2434-3+deb11u1) ...#9 5.467 Selecting previously unselected package vim.#9 5.468 Preparing to unpack .../vim_2%3a8.2.2434-3+deb11u1_amd64.deb ...#9 5.473 Unpacking vim (2:8.2.2434-3+deb11u1) ...#9 5.573 Setting up libgpm2:amd64 (1.20.7-8) ...#9 5.578 Setting up xxd (2:8.2.2434-3+deb11u1) ...#9 5.583 Setting up vim-common (2:8.2.2434-3+deb11u1) ...#9 5.593 Setting up vim-runtime (2:8.2.2434-3+deb11u1) ...#9 5.640 Setting up vim (2:8.2.2434-3+deb11u1) ...#9 5.644 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vim (vim) in auto mode#9 5.646 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vimdiff (vimdiff) in auto mode#9 5.648 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rvim (rvim) in auto mode#9 5.650 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rview (rview) in auto mode#9 5.652 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vi (vi) in auto mode#9 5.655 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/view (view) in auto mode#9 5.657 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/ex (ex) in auto mode#9 5.660 update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in auto mode#9 5.667 Processing triggers for libc-bin (2.31-13+deb11u5) ...#9 DONE 5.7s#10 exporting to image#10 exporting layers#10 exporting layers 0.4s done#10 writing image sha256:bc054c50e052f8413c89a458d7bdab80529d86449c52f9b590a5289e3ea97308 done#10 naming to docker.io/library/heredoc:test done#10 DONE 0.4s

But this is actually run by /bin/sh not bash because when I use bash features like this:

# syntax=docker/dockerfile:1FROM debianRUN <<EOT bashto_install=(vim)apt-get updateapt-get install -y "${to_install[@]}"EOT

I get this error:

$ docker buildx build --file ./.docker/Dockerfile-test --no-cache --progress=plain --secret id=composer-auth,src=$(pwd)/auth.json --tag heredoc:test .#1 [internal] load build definition from Dockerfile-test#1 transferring dockerfile:#1 transferring dockerfile: 188B done#1 DONE 0.0s......#8 [1/2] FROM docker.io/library/debian@sha256:534da5794e770279c889daa891f46f5a530b0c5de8bfbc5e40394a0164d9fa87#8 CACHED#9 [2/2] RUN <<EOT bash#9 0.268 /bin/sh: 1: Bad substitution#9 ERROR: executor failed running [/bin/sh -c <<EOT bashto_install=(vim)apt-get updateapt-get install -y "${to_install[@]}"EOT]: exit code: 2------> [2/2] RUN <<EOT bash:#9 0.268 /bin/sh: 1: Bad substitution------error: failed to solve: executor failed running [/bin/sh -c <<EOT bashto_install=(vim)apt-get updateapt-get install -y "${to_install[@]}"EOT]: exit code: 2

I also tried using #!/bin/bash -ce at the beginning of heredoc string but it still was using /bin/sh. Only changing shell in dockerfile like:

SHELL ["/bin/bash", "-ce"]

works as expected.

Is this example misleading or I'm doing something wrong that bash does not work for me with example from official docs?

2

Best Answer


The issue is that the $ is being evaluated in the sh shell which is running the EOT command. Escaping it resolves the issue:

RUN <<EOT bashto_install=(vim)apt-get updateapt-get install -y "\${to_install[@]}"EOT

Please put set -eu at the beginning of the heredoc.

Else builds can succeed when they must fail.

FROM debianRUN <<EOT bashset -euxto_install=(vim)apt-get updatefailing_command # set -e exit with non-zero status apt-get install -y "\${to_install[@]}"EOT

set -e or set --errexit = If any command fail the script will exit with non-zero value
set -u or set --nounset = If a variable is unset it will exit with a non zero status
set -x or set --xtrace = Useful for debugging, print + and the executed command.enter image description here