Skip to content

feat(docker): add support for sandboxes from docker archives#2361

Open
feloy wants to merge 3 commits into
NVIDIA:mainfrom
feloy:docker-archive
Open

feat(docker): add support for sandboxes from docker archives#2361
feloy wants to merge 3 commits into
NVIDIA:mainfrom
feloy:docker-archive

Conversation

@feloy

@feloy feloy commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Add support for Docker archives (.tar, .tar.gz, .tgz) as --from sources on sandbox create. This enables air-gapped environments and VM-based build pipelines where a Docker archive is the natural hand-off artifact.

Related Issue

Closes #2175

Changes

  • Add DockerArchive { path } variant to ResolvedSource in the CLI
  • Detect .tar/.tar.gz/.tgz files in resolve_from() via extension-based matching (consistent with the existing Dockerfile filename heuristic)
  • Add load_docker_archive() in openshell-bootstrap using Bollard's import_image() (POST /images/load), parsing the image tag from Docker's "Loaded image: <tag>" response
  • Add load_from_docker_archive() in the CLI with the same local-gateway guard as Dockerfile sources
  • Update --from help text and published docs to mention Docker archive support

No proto, gateway, or server changes — the archive's embedded tag flows through the existing SandboxTemplate.image field.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
    • resolve_from correctly classifies .tar, .tar.gz, .tgz files as DockerArchive
    • Missing archive paths produce the expected local error
    • filename_looks_like_docker_archive detects valid extensions (case-insensitive) and rejects non-archives
  • E2E tests added/updated (if applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

@feloy
feloy requested review from a team, derekwaynecarr, maxamillion and mrunalp as code owners July 20, 2026 08:46
@copy-pr-bot

copy-pr-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@drew drew changed the title Docker archive feat(docker): add support for sandboxes from docker archives Jul 20, 2026
Comment thread crates/openshell-cli/src/run.rs Outdated
Comment on lines +2635 to +2638
lower.ends_with(".tar.gz")
|| Path::new(&*lower)
.extension()
.is_some_and(|ext| ext == "tar" || ext == "tgz")

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.

Question: Why use ends_with in one case and `.extension for the other? Can we use the same logic for all cases?

Also we already have a path? Can't we call .extension() directly on that and then map the resultant string through to_lowercase()?

Comment thread crates/openshell-cli/src/run.rs Outdated
Comment on lines +2532 to +2533
/// 1. Existing file whose name contains "Dockerfile" → build from file.
/// 1b. Existing file with `.tar`/`.tar.gz`/`.tgz` extension → load archive.

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.

Split into: A single heading like "Existing file" followed by two points indicating the different sources.

/// Resolution order:
/// 1. Existing file whose name contains "Dockerfile" → build from file.
/// 1b. Existing file with `.tar`/`.tar.gz`/`.tgz` extension → load archive.
/// 2. Existing directory that contains a `Dockerfile` → build from directory.

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.

Question: Why are two "Dockerfile" cases now separated by a archive case? Should the explicit Dockerfile and Dockerfile parent folder cases not be treated the same way?

Comment thread crates/openshell-cli/src/run.rs Outdated
/// 1. Existing file whose name contains "Dockerfile" → build from file.
/// 1b. Existing file with `.tar`/`.tar.gz`/`.tgz` extension → load archive.
/// 2. Existing directory that contains a `Dockerfile` → build from directory.
/// 3. Missing explicit local paths → local error, not image pull.

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.

Out-of-scope: What are "Missing explicit local paths"?

Comment thread crates/openshell-cli/src/run.rs Outdated
@@ -2610,6 +2625,19 @@ fn filename_looks_like_dockerfile(path: &Path) -> bool {
lower.contains("dockerfile") || lower.ends_with(".dockerfile")

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.

Out of scope, but the || lower.ends_with(".dockerfile") is redundant since lower.contains("dockerfile")` will ALWAYS be true in that case.

@elezar elezar left a comment

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.

Thanks for adding archive-based sandbox sources. I think we need to address two issues before merging this:

  1. Image ingestion is not compute-driver aware. The current check only distinguishes local from remote gateways. Both Dockerfile builds and this new archive loader use Bollard’s local Docker connection, then assume the resulting image is visible to the active driver. That works reliably for Docker, but not for Podman unless the CLI’s DOCKER_HOST happens to point to the exact Podman socket used by the gateway. The VM driver also has its own Docker/Podman resolution behavior. We should either introduce a driver-aware image-ingestion path or explicitly restrict local Dockerfile/archive sources to confirmed Docker-backed gateways.

  2. The archive is buffered entirely in memory. std::fs::read() synchronously loads the complete archive before uploading it. Container archives can be several gigabytes. Bollard provides import_image_stream(), which should be used with an asynchronous, chunked file stream.

These are closely related: Dockerfiles and archives are both ways of ingesting a local image into the active compute runtime. I suggest establishing that capability boundary first, then implementing archive loading through it. A smaller alternative would be to scope this PR explicitly to Docker-backed gateways, reject other drivers clearly, and still switch the upload to the streaming API.

@feloy

feloy commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for adding archive-based sandbox sources. I think we need to address two issues before merging this:

  1. Image ingestion is not compute-driver aware. The current check only distinguishes local from remote gateways. Both Dockerfile builds and this new archive loader use Bollard’s local Docker connection, then assume the resulting image is visible to the active driver. That works reliably for Docker, but not for Podman unless the CLI’s DOCKER_HOST happens to point to the exact Podman socket used by the gateway. The VM driver also has its own Docker/Podman resolution behavior. We should either introduce a driver-aware image-ingestion path or explicitly restrict local Dockerfile/archive sources to confirmed Docker-backed gateways.

This makes me realise that the main use case for this feature is for systems not having docker/podman available, and using VMs. So I'll change for supporting VM driver first.

Having to import a Docker archive in this case is not straightforward, as the layers need to be flattened, including whiteout handling. I'll also consider switching to importing another format with an already flattened filesystem (docker/podman export).

feloy added 2 commits July 22, 2026 12:26
Add support for loading pre-built Docker archives into the local daemon
when creating sandboxes, enabling air-gapped and macOS VM-based build
pipelines where a Docker archive is the natural hand-off artifact.

Closes NVIDIA#2175

Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Verify the full round-trip: docker build → docker save → openshell
sandbox create --from archive.tar → marker file present in output.

Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
…passthrough to VM driver

Instead of loading Docker archives into the local daemon via `docker load`,
tar archives passed via `--from` are now treated as flat rootfs tars and
forwarded to the VM compute driver through `driver_config.rootfs_tar_path`.
This removes the bollard `import_image` dependency and aligns the archive
flow with the VM driver's native rootfs overlay mechanism.

Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
@feloy
feloy requested review from elezar and ericcurtin July 22, 2026 10:32
@elezar

elezar commented Jul 22, 2026

Copy link
Copy Markdown
Member

This makes me realise that the main use case for this feature is for systems not having docker/podman available, and using VMs. So I'll change for supporting VM driver first.

Note that it's not REQUIRED to make this work for VMs, but we should restrict the use of archives to Docker/Podman in the first iteration.

@feloy

feloy commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

This makes me realise that the main use case for this feature is for systems not having docker/podman available, and using VMs. So I'll change for supporting VM driver first.

Note that it's not REQUIRED to make this work for VMs, but we should restrict the use of archives to Docker/Podman in the first iteration.

In fact, the primary use case of the issue #2175 is a system without docker/podman which cannot build OCI images, and because of this restriction, relying on a docker/podman driver is not consistent. I modified the issue to make this point clearer.

To provide the full context, our use case would be to build the image using a microVM (poc: https://github.com/feloy/krun-build-image), and using a VM as OpenShell driver.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support rootfs tar as --from source for VM driver sandboxes

3 participants