#7103·buildah

Bad handling of variables in Heredocs (at least with COPY)

Author: FoxCieCreated Sep 11, 2026Updated Sep 11, 2026
Labelskind/bug

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 :

bash
export VAR="EXPANDED"
cat <<EOF
"$VAR" '$VAR'
EOF

gives "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.sh

gives :

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

  1. Create a Containerfile with one of the example given above
  2. 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'
EOF

In 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

yaml
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/amd64

buildah info output

yaml
{
    "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

toml
Default from Fedora 44.

Upstream Latest Release

No

Additional environment details

No response

Additional information

No response

Source: podman-container-tools/buildah