Summary
The nerdctl container commit command hardcodes a 1-hour lease expiration via leases.WithExpiration(1*time.Hour). When committing a container with a large filesystem delta (e.g., many packages installed), the diff.Compare operation can exceed 1 hour, causing the containerd GC to clean up the expired lease and its associated ingest data. This results in a context deadline exceeded error.
Evidence
1. The hardcoded 1-hour lease in nerdctl
File: pkg/imgutil/commit/commit.go:177-178
// Don't gc me and clean the dirty data after 1 hour!
ctx, done, err := client.WithLease(ctx, leases.WithRandomID(), leases.WithExpiration(1*time.Hour))
This ctx with the 1-hour lease is then passed to createDiff() at line 190, which calls containerd's rootfs.CreateDiff() → diff.Compare().
2. The call chain
nerdctl container commit
→ commit.Commit() [commit.go:80]
→ client.WithLease(ctx, leases.WithExpiration(1h)) [commit.go:178] ← 1-hour lease!
→ createDiff(ctx, ...) [commit.go:190]
→ rootfs.CreateDiff(ctx, ...) [commit.go:423]
→ differ.Compare(ctx, lower, upper) [containerd rootfs/diff.go:66]
→ archive.WriteDiff(ctx, ...) [containerd walking/differ.go:151]
3. No timeout override in the CLI
File: pkg/cmd/container/commit.go
The commitOptions() function (line 57) does not expose any timeout or lease expiration flag. The CLI command definition has no --timeout flag. Users cannot increase the lease expiration.
4. How it fails
- Containerd's GC periodically scans for expired leases and removes their associated resources (content store ingest data, snapshots).
- When
diff.Compare is still running and the 1-hour lease expires, the GC deletes the in-progress ingest data.
- The next write to the content writer fails or the commit step fails, producing a
context deadline exceeded error.
Impact
- Users with containers that have large filesystem changes (e.g., installing many packages, large data writes) cannot reliably commit them.
- The 1-hour limit is not documented in the CLI help or the
nerdctl commit documentation.
- There is no way to override this limit from the CLI.
Proposed Fix
- Add a
--timeout flag to nerdctl container commit that allows users to set the lease expiration duration.
- Increase the default lease expiration from 1 hour to a more reasonable value (e.g., 24 hours, matching containerd's own default in
client/lease.go).
- Document the timeout behavior and the new flag.
Related Code Locations
| Location |
Description |
pkg/imgutil/commit/commit.go:177-178 |
Hardcoded leases.WithExpiration(1*time.Hour) |
pkg/cmd/container/commit.go:57 |
commitOptions() — no timeout flag |
pkg/api/types/container_types.go:408 |
ContainerCommitOptions struct — no timeout field |
Environment
- nerdctl: latest (main branch)
- containerd: any version (the diff timeout is not in containerd, it's purely in nerdctl)
Summary
The
nerdctl container commitcommand hardcodes a 1-hour lease expiration vialeases.WithExpiration(1*time.Hour). When committing a container with a large filesystem delta (e.g., many packages installed), thediff.Compareoperation can exceed 1 hour, causing the containerd GC to clean up the expired lease and its associated ingest data. This results in acontext deadline exceedederror.Evidence
1. The hardcoded 1-hour lease in nerdctl
File:
pkg/imgutil/commit/commit.go:177-178This
ctxwith the 1-hour lease is then passed tocreateDiff()at line 190, which calls containerd'srootfs.CreateDiff()→diff.Compare().2. The call chain
3. No timeout override in the CLI
File:
pkg/cmd/container/commit.goThe
commitOptions()function (line 57) does not expose any timeout or lease expiration flag. The CLI command definition has no--timeoutflag. Users cannot increase the lease expiration.4. How it fails
diff.Compareis still running and the 1-hour lease expires, the GC deletes the in-progress ingest data.context deadline exceedederror.Impact
nerdctl commitdocumentation.Proposed Fix
--timeoutflag tonerdctl container committhat allows users to set the lease expiration duration.client/lease.go).Related Code Locations
pkg/imgutil/commit/commit.go:177-178leases.WithExpiration(1*time.Hour)pkg/cmd/container/commit.go:57commitOptions()— no timeout flagpkg/api/types/container_types.go:408ContainerCommitOptionsstruct — no timeout fieldEnvironment