microsoft/new-overlay-image
> How to create overlay image packages that layer on top of base images. Use when creating a new overlay package like quicksand-agent, scaffolding an overlay, or modifying hatch_build.py for overlay builds.
npx skills add https://github.com/microsoft/quicksand --skill new-overlay-image
Overlay packages (like quicksand-agent) boot a base VM, install software, and save the result as an overlay. They're faster to create than base images and layer on top of existing base images.
Use the scaffold tool:
quicksand dev scaffold overlay my-overlay-package --base ubuntu
This creates my-overlay-package/ from the overlay template and renames everything. Then edit the _setup() function in hatch_build.py.
{name}/
├── pyproject.toml
├── hatch_build.py # Boots VM, runs setup, saves overlay
├── README.md
└── {module_name}/
├── __init__.py # ImageProvider entry point
├── sandbox.py # Optional: custom Sandbox subclass
└── images/ # Built overlay save (git-ignored)
├── manifest.json
└── overlays/*.qcow2
During uv build, the hatch_build.py hook:
_setup(shell) — your install commandsReference: packages/contrib/quicksand-agent/hatch_build.py
The key function to edit:
async def _setup(shell: Shell) -> None:
"""Install steps for your overlay."""
await shell("apt-get update", timeout=120)
await shell("apt-get install -y your-packages", timeout=300)
await shell("pip install your-python-deps", timeout=300)
The shell callable runs commands inside the VM, streams output, and raises on failure. Use timeout= for slow commands.
The build hook also:
pure_python = False and platform-specific wheel tagsimages/manifest.json already existsUbuntuSandboxConfig with 4G memory, 4 CPUs, full network, 10G diskReference: packages/contrib/quicksand-agent/quicksand_agent/__init__.py
Must export:
image — _ImageProvider instance that resolves the bundled save via ImageResolver()._resolve_save(IMAGES_DIR)[project]
name = "{name}"
version = "0.1.0"
dependencies = [
"quicksand-core>=0.6.0",
"quicksand-ubuntu>=0.5.0",
]
[build-system]
requires = ["hatchling", "quicksand-core", "quicksand-ubuntu", "quicksand-qemu", "quicksand-smb"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["{module_name}"]
artifacts = [
"{module_name}/images/manifest.json",
"{module_name}/images/overlays/*.qcow2",
]
[tool.hatch.build.targets.wheel.hooks.custom]
[project.entry-points."quicksand.images"]
{name} = "{module_name}:image"
[tool.uv.sources]
quicksand-core = { workspace = true }
quicksand-ubuntu = { workspace = true }
Add the package to _shared.py:OVERLAY_PACKAGES so the release pipeline:
# First build (boots VM, runs setup, saves overlay — takes a few minutes)
uv build --package {name}
# Subsequent builds (reuses cached overlay)
uv build --package {name}
To force rebuild, delete the images directory:
rm -rf packages/contrib/{name}/{module_name}/images/
uv build --package {name}
uv run quicksand run {name}
from quicksand_core import Sandbox
async with Sandbox(image="{name}") as sb:
result = await sb.execute("your-command")
print(result.stdout)
By default overlays use Ubuntu. To use Alpine or another base:
hatch_build.py from quicksand_ubuntu to your basepyproject.toml dependencies and build-system requires_setup() commands for the new distro (e.g., apk add instead of apt-get install)Take microsoft/new-overlay-image from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip, apt.
Without those the skill loads but fails at the first command.