Skip to content

fix(bootstrap): stream image push through temp file to prevent OOM#700

Merged
drew merged 1 commit intomainfrom
602-streaming-image-push/drew
Mar 31, 2026
Merged

fix(bootstrap): stream image push through temp file to prevent OOM#700
drew merged 1 commit intomainfrom
602-streaming-image-push/drew

Conversation

@drew
Copy link
Copy Markdown
Collaborator

@drew drew commented Mar 31, 2026

Summary

Fix unbounded memory allocation during openshell sandbox create --from image push that causes OOM kills on images >1-2 GB. The image push pipeline now streams through a temp file with ~8 MiB constant memory usage instead of buffering 3x the image size in RAM.

Related Issue

Closes #602
Closes #655

Changes

  • crates/openshell-bootstrap/src/push.rs: Rewrote the image push pipeline:
    • collect_export()export_to_tempfile(): Streams docker.export_images() directly to a NamedTempFile instead of collecting into Vec<u8>
    • wrap_in_tar() + upload_archive()streaming_tar_upload(): Returns a Stream that yields the outer tar (512-byte header → 8 MiB file chunks → padding/EOF blocks) using async_stream::try_stream!
    • Upload uses bollard::body_try_stream() instead of body_full(), streaming directly into upload_to_container()
    • Added incremental export progress reporting every 100 MiB (previously only reported after full export)
  • crates/openshell-bootstrap/Cargo.toml: Promoted tempfile from dev-dependency to dependency; added async-stream

Memory profile comparison

Image size Before (peak RSS) After (peak RSS)
2.3 GB ~7 GB (3x) ~8 MiB
3.7 GB ~11 GB (3x) ~8 MiB

Testing

  • mise run pre-commit passes (lint, format, license headers, tests)
  • All 89 openshell-bootstrap unit tests pass
  • Zero new clippy warnings on changed files
  • E2E tests with large images (requires gateway environment)

Checklist

  • Follows Conventional Commits
  • Public API unchanged — no caller modifications needed
  • Architecture docs updated (if applicable)

The image push pipeline buffered the entire Docker image tar 3x in
memory (export, tar wrap, Bytes copy), causing OOM kills for images
over ~1-2 GB. Replace the in-memory pipeline with a temp-file +
streaming upload: export to a NamedTempFile, then stream the outer
tar (header, 8 MiB file chunks, footer) directly into
upload_to_container via body_try_stream. Peak memory drops from ~3x
image size to ~8 MiB constant.

Also adds incremental export progress reporting every 100 MiB.
@drew drew requested a review from a team as a code owner March 31, 2026 15:43
@drew drew self-assigned this Mar 31, 2026
@drew drew merged commit a1e1d54 into main Mar 31, 2026
10 checks passed
@drew drew deleted the 602-streaming-image-push/drew branch March 31, 2026 19:07
@minhdqdev
Copy link
Copy Markdown
Contributor

Great! Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants