FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(image): support --change on import by mayur-tolexo · Pull Request #5102 · containerd/nerdctl · GitHub

feat(image): support --change on import - #5102

Open
mayur-tolexo wants to merge 1 commit into
containerd:mainfrom
mayur-tolexo:feat/import-change
Open

mayur-tolexo wants to merge 1 commit into
containerd:mainfrom
mayur-tolexo:feat/import-change

Conversation

Copy link
Copy Markdown
Contributor

docker import --change applies Dockerfile instructions to the config of the image it creates; nerdctl had it listed as unimplemented (part of #3867). This implements it.

nerdctl import --change 'CMD ["echo"]' --change 'ENV FOO=bar' rootfs.tar img now behaves like Docker. Supported instructions are the ones representable in the OCI image config that import writes: CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, USER, VOLUME, WORKDIR, STOPSIGNAL. HEALTHCHECK, ONBUILD and SHELL only exist in Docker's own image-config schema, not the OCI one, so they're rejected with a clear error instead of being silently dropped.

--change only makes sense when building a fresh config, so it applies to a filesystem (rootfs) import and is rejected for a standard image archive that already carries its own config.

Checked against Docker 29.4.0 in a Linux/containerd sandbox — inspect .Config comes out identical for the supported instructions:

$ nerdctl import --change 'CMD ["echo","hi"]' --change 'ENTRYPOINT ["/bin/sh","-c"]' \
    --change 'ENV FOO=bar BAZ=qux' --change 'LABEL role=demo' --change 'WORKDIR /srv' \
    --change 'EXPOSE 8080 53/udp' --change 'USER nobody' --change 'VOLUME ["/data"]' \
    rootfs.tar changeimg
$ nerdctl image inspect changeimg --format '{{json .Config}}'
{"User":"nobody","ExposedPorts":{"53/udp":{},"8080/tcp":{}},"Env":["FOO=bar","BAZ=qux"],"Cmd":["echo","hi"],"Volumes":{"/data":{}},"WorkingDir":"/srv","Entrypoint":["/bin/sh","-c"],"Labels":{"role":"demo"}}

docker gives the same config for those inputs. The unsupported ones error clearly:

$ nerdctl import --change 'HEALTHCHECK CMD true' rootfs.tar img
FATA[0000] invalid --change "HEALTHCHECK CMD true": the HEALTHCHECK instruction is not supported by import

The parser has unit tests per instruction (JSON vs shell form, quoted ENV/LABEL values, and the error paths) plus an integration test that imports with --change and checks the resulting config.

mayur-tolexo marked this pull request as draft July 25, 2026 09:13

Copy link
Copy Markdown
Contributor Author

Ran every --change instruction through both nerdctl (this branch, built in a Linux/containerd sandbox) and Docker 29.4.0 with identical inputs.

All supported instructions — inspect .Config is identical.

$ nerdctl import \
    --change 'CMD ["echo","hi"]' --change 'ENTRYPOINT ["/bin/sh","-c"]' \
    --change 'ENV FOO=bar BAZ=qux' --change 'ENV SINGLE hello world' \
    --change 'LABEL role=demo tier="two words"' --change 'EXPOSE 8080 53/udp' \
    --change 'USER nobody:nogroup' --change 'VOLUME ["/data","/cache"]' \
    --change 'WORKDIR /srv' rootfs.tar combimg
$ nerdctl image inspect combimg --format '{{json .Config}}'
{"User":"nobody:nogroup","AttachStdin":false,"ExposedPorts":{"53/udp":{},"8080/tcp":{}},"Env":["FOO=bar","BAZ=qux","SINGLE=hello world"],"Cmd":["echo","hi"],"Image":"docker.io/library/combimg:latest","Volumes":{"/cache":{},"/data":{}},"WorkingDir":"/srv","Entrypoint":["/bin/sh","-c"],"Labels":{"role":"demo","tier":"two words"}}

Docker, same command:

$ docker image inspect combimg --format '{{json .Config}}'
{"User":"nobody:nogroup","ExposedPorts":{"53/udp":{},"8080/tcp":{}},"Env":["FOO=bar","BAZ=qux","SINGLE=hello world"],"Entrypoint":["/bin/sh","-c"],"Cmd":["echo","hi"],"Volumes":{"/cache":{},"/data":{}},"WorkingDir":"/srv","Labels":{"role":"demo","tier":"two words"}}

Same User, both ENV forms (k=v pairs and the legacy key value), the quoted LABEL value, EXPOSE port/proto, VOLUME, WORKDIR, CMD, ENTRYPOINT. (nerdctl's dockercompat output carries a couple of harmless extra keys such as Image/AttachStdin; the config values themselves match.)

Shell-form CMD/ENTRYPOINT — both wrap in /bin/sh -c.

$ nerdctl import --change 'CMD echo hi there' --change 'ENTRYPOINT /entrypoint.sh --flag' rootfs.tar shellimg
$ nerdctl image inspect shellimg --format '{{json .Config}}'
{"AttachStdin":false,"Cmd":["/bin/sh","-c","echo hi there"],"Image":"docker.io/library/shellimg:latest","Entrypoint":["/bin/sh","-c","/entrypoint.sh --flag"]}

$ docker image inspect shellimg --format '{{json .Config}}'
{"Entrypoint":["/bin/sh","-c","/entrypoint.sh --flag"],"Cmd":["/bin/sh","-c","echo hi there"]}

Instructions not representable in the OCI image config — nerdctl rejects them:

$ nerdctl import --change 'HEALTHCHECK CMD true' rootfs.tar x
FATA[0000] invalid --change "HEALTHCHECK CMD true": the HEALTHCHECK instruction is not supported by import
$ nerdctl import --change 'ONBUILD RUN true' rootfs.tar x
FATA[0000] invalid --change "ONBUILD RUN true": the ONBUILD instruction is not supported by import
$ nerdctl import --change 'SHELL ["/bin/bash","-c"]' rootfs.tar x
FATA[0000] invalid --change "SHELL [\"/bin/bash\",\"-c\"]": the SHELL instruction is not supported by import
$ nerdctl import --change 'RUN echo x' rootfs.tar x
FATA[0000] invalid --change "RUN echo x": unknown instruction "RUN"

Docker rejects SHELL and RUN too (SHELL/RUN is not a valid change command). It differs only on HEALTHCHECK and ONBUILD, which it accepts because its own image-config schema carries those fields — the OCI config that import writes has no place for them, so nerdctl rejects rather than silently dropping.

mayur-tolexo force-pushed the feat/import-change branch 2 times, most recently from ccf77bf to fdf7dbf Compare July 30, 2026 09:30
mayur-tolexo marked this pull request as ready for review August 4, 2026 12:46
Apply Dockerfile-style instructions to the config of the image created by
`nerdctl import`, matching `docker import --change`. Supported instructions are
the ones representable in the OCI image config: CMD, ENTRYPOINT, ENV, EXPOSE,
LABEL, USER, VOLUME, WORKDIR, STOPSIGNAL. HEALTHCHECK, ONBUILD and SHELL only
exist in Docker's config schema and are rejected with a clear error.

--change applies to a filesystem (rootfs) import, which builds a fresh config;
it is rejected for a standard image archive that already carries its own config.

Part of containerd#3867

Signed-off-by: Mayur Das <mayur.das@neevcloud.com>
AkihiroSuda added this to the v2.4.0 milestone Aug 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Can we invoke buildctl build ?

mayur-tolexo Sep 23, 2026
edited
Loading

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Tried it.

The Dockerfile has to be FROM <the image we just imported>, which only resolves on a containerd-worker buildkitd in the same namespace and snapshotter and import needs no daemon today.

Worse, BuildKit runs everything we pass it:

$ nerdctl import --change 'RUN touch /pwned' rootfs.tar img
docker.io/library/img:latest
$ nerdctl run --rm --entrypoint /bin/ls img -la /pwned
-rw-r--r-- 1 0 0 0 Sep 23 09:20 /pwned

FROM alpine is accepted too and replaces the rootfs, so we'd still need an allow-list here. It also injects a default PATH and adds a layer for WORKDIR.

Using BuildKit's Dockerfile parser in-process instead gives the same config with no daemon, and the type switch is the allow-list. moby/buildkit is already in the module graph. Would that work for you?

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL