Bad handling of variables in Heredocs (at least with COPY)
Issue Description
It feels like handling of build args (and maybe env variables too) inside heredoc COPY instructions is very weird and sometimes it honestly does not make sense to me.
Several issues related to this have already been reported, for instance https://github.com/podman-container-tools/buildah/issues/6048 , https://github.com/podman-container-tools/buildah/issues/5422 , https://github.com/podman-container-tools/buildah/issues/6044 , but I did not find any that talked about what really bothers me.
I feel like build variables (and maybe env vars too, but it could make sense to treat them differently) should always be expanded in heredocs, but this is not the case.
When running a shell command with an heredoc, variables are always expanded, for instance :
export VAR="EXPANDED"
cat <<EOF
"$VAR" '$VAR'
EOFgives "EXPANDED" 'EXPANDED'.
But, when doing something similar when building, it behaves very oddly :
ARG VAR="EXPANDED"
RUN <<EOF
echo "VAR should be expanded: $VAR"
echo 'VAR should also be expanded': $VAR'The result is :
STEP 1/3: FROM alpine
STEP 2/3: ARG VAR="EXPANDED"
STEP 3/3: RUN <<EOF (echo "VAR should be expanded: $VAR"...)
VAR should be expanded: EXPANDED
VAR should also be expanded?: $VAR
COMMIT
...The same happens with COPY :
ARG VAR="EXPANDED"
COPY <<EOF /tmp/script.sh
#!/bin/sh
echo "VAR should be expanded: $VAR"
echo 'VAR should also be expanded?: $VAR'
EOF
RUN cat /tmp/script.shgives :
STEP 1/4: FROM alpine
STEP 2/4: ARG VAR="EXPANDED"
STEP 3/4: COPY <<EOF /tmp/script.sh (#!/bin/sh...)
STEP 4/4: RUN cat /tmp/script.sh
#!/bin/sh
echo "VAR should be expanded: EXPANDED"
echo 'VAR should also be expanded?: $VAR'
COMMIT
...Steps to reproduce the issue
Steps to reproduce the issue
- Create a Containerfile with one of the example given above
- build (
buildah build .)
Describe the results you received
We can observe that the output is very odd, since variable expansion occurs according to shell rules (inside double quotes but not simple quotes), but the variable is a build variable, which doest not exist at runtime, so it should always be expanded at build time, by the build process.
I feel like it really is unexpected, as building the same examples with Buildkit does expand the variable everywhere, as expected.
Describe the results you expected
The variable should be expanded everywhere, not only inside double quotes, because those quotes should have no syntactic meaning inside heredocs.
From what I have seen, it seems that variable expansion is handled by the invoked process (i.e. the shell), but this is not how heredocs are supposed to work, or at least it is not how it behaves with shells, and leads to unexpected behaviour.
I feel like it really is unexpected, as building the same examples with Buildkit does expand the variable everywhere, as expected.
To go a little bit further, I feel like there should be a difference between :
RUN <<EOF
echo '$VAR'and :
RUN cat <<EOF
'$VAR'
EOFIn the first example, the heredoc is a Containerfile heredoc, that should be treated by the build process, whereas in the second it makes sense to consider that the heredoc is a heredoc for the shell running the command. At this point, there could also be a different behaviour if VAR is a build variable or an env variable (like preprocessing all build vars before sending the heredoc to the shell that will treat env vars, which could be tricky with for instance BUILD_ARG='$ENV_VAR' and ENV_VAR='EXPANDED', should a heredoc invoking cat with $BUILD_ARG display EXPANDED?)
Apart from this specific odd behaviour, trying to at least be closer to the behaviour of Buildkit would be great, since we use this kind of heredocs to generate scripts injecting directories into env vars before invoking a command, that work great with Buildkit but are just broken with $DIRECTORY instead of the actual path when building with buildah.
buildah version output
Version: 1.43.2
Go Version: go1.26.4-X:nodwarf5
Image Spec: 1.1.1
Runtime Spec: 1.2.1
CNI Spec: 1.1.0
libcni Version:
image Version: 5.39.2
Git Commit:
Built: Fri Jun 19 17:11:44 2026
OS/Arch: linux/amd64
BuildPlatform: linux/amd64buildah info output
{
"host": {
"CgroupVersion": "v2",
"Distribution": {
"distribution": "fedora",
"version": "44"
},
"MemFree": 2292412416,
"MemTotal": 67140161536,
"OCIRuntime": "crun",
"SwapFree": 9829928960,
"SwapTotal": 17163083776,
"arch": "amd64",
"cpus": 20,
"hostname": "matthieu-tuxedo",
"kernel": "7.1.8-200.fc44.x86_64",
"os": "linux",
"rootless": true,
"uptime": "357h 49m 46.6s (Approximately 14.88 days)",
"variant": ""
},
"store": {
"ContainerStore": {
"number": 160
},
"GraphDriverName": "overlay",
"GraphImageStore": "",
"GraphOptions": null,
"GraphRoot": "/home/foxcie/.local/share/containers/storage",
"GraphStatus": {
"Backing Filesystem": "extfs",
"Native Overlay Diff": "true",
"Supports d_type": "true",
"Supports shifting": "false",
"Supports volatile": "true",
"Using metacopy": "false"
},
"GraphTransientStore": false,
"ImageStore": {
"number": 1152
},
"RunRoot": "/run/user/1000/containers"
}
}Provide your storage.conf
Default from Fedora 44.Upstream Latest Release
No
Additional environment details
No response
Additional information
No response
Source: podman-container-tools/buildah