Skip to content

nerdctl save -o / export -o do not truncate an existing file #5159

Closed

Description

@ekalinin

Description

nerdctl save -o FILE and nerdctl export -o FILE open the output with os.O_CREATE|os.O_WRONLY and no os.O_TRUNC. When FILE already exists and is longer than what is about to be written, the tail of the previous content survives past the end of the new archive.

docker save does not have this problem: it writes through an atomic writer (temp file plus rename), so the destination is replaced wholesale.

// docker/cli cli/command/image/save.go
writer, err := atomicwriter.New(opts.output, 0o600)

Steps to reproduce the issue

  1. nerdctl save -o out.tar <a larger image>
  2. nerdctl save -o out.tar <a smaller image>
  3. ls -l out.tar — it still has the size of the archive from step 1.

The same applies to nerdctl export -o out.tar <container>.

The open flags in isolation:

func open(path string) (*os.File, error) {
	// as in image_save.go and container_export.go
	return os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
}

f, _ := open(path)
f.Write([]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")) // 30 bytes, the larger image
f.Close()

f, _ = open(path)
f.Write([]byte("BBBBBBBBBB")) // 10 bytes, the smaller image
f.Close()
wrote 10 bytes, file holds 30: "BBBBBBBBBBAAAAAAAAAAAAAAAAAAAA"

Describe the results you received and expected

Received: the file keeps the size of the previous, larger archive, with the leftover bytes of that archive sitting after the end of the new one.

Note that this does not break nerdctl load: a tar reader stops at the end-of-archive marker and never looks at what follows, which I checked with both archive/tar and the system tar. What is wrong is the artifact itself — it is larger than the archive it is supposed to contain, and the extra bytes belong to an unrelated image, which shows up in anything that checksums the file, accounts for its size, or ships it somewhere.

Expected: -o replaces the file, as docker save -o and docker export -o do, and as a shell redirect does.

pkg/healthcheck/log.go opens a file with the same flags, but seeks to the end and appends on purpose, so it is not affected.

What version of nerdctl are you using?

main, at c235f00.

The flags came in with 4b8061d (2024-08-30) for save and 3e50703 (2025-08-05) for export.

Activity

  1. ekalinin commented on Aug 24, 2026

    @ekalinin
    ContributorAuthor

    Corrected the description above: I first wrote that the resulting file is not a valid tar, which is wrong. A tar reader stops at the end-of-archive marker, so nerdctl load reads such a file without complaining — I verified that with both archive/tar and the system tar. The defect is the artifact carrying the tail of an unrelated archive, not a read failure.

  2. added a commit that references this issue on Aug 27, 2026
    40da4a4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions