microsoft/new-base-image
> How to create base image packages that bundle a VM image built from a Dockerfile. Use when creating a new distro base like quicksand-ubuntu or quicksand-alpine, scaffolding a base image, or modifying Dockerfiles for base image builds.
npx skills add https://github.com/microsoft/quicksand --skill new-base-image
Base image packages (like quicksand-ubuntu, quicksand-alpine) bundle a VM image built from a Dockerfile.
Use the scaffolding tool:
quicksand dev scaffold base quicksand-{distro}
This creates quicksand-{distro}/ from the base image template and renames everything. Then customize the Dockerfile and build with uv build.
quicksand-{distro}/
├── pyproject.toml
├── hatch_build.py # Builds image during `uv build`
├── README.md
└── quicksand_{distro}/
├── __init__.py # Python API
├── images/ # Built images (git-ignored)
└── docker/
├── Dockerfile
├── quicksand-sudoers
├── hostname
└── [init configs]
The guest agent source is copied into docker/agent/ automatically at build time by quicksand-image-tools.
Every base image MUST include:
# Ubuntu
RUN apt-get install -y linux-image-virtual \
&& KVER=$(ls /lib/modules/ | head -1) \
&& apt-get install -y initramfs-tools \
&& update-initramfs -c -k ${KVER}
# Alpine
RUN apk add --no-cache linux-virt
# Ubuntu (systemd)
RUN apt-get install -y systemd systemd-sysv
# Alpine (OpenRC)
RUN apk add --no-cache openrc busybox-openrc
# 9p for host file sharing
RUN echo "9p" >> /etc/modules && echo "9pnet" >> /etc/modules && echo "9pnet_virtio" >> /etc/modules
# quicksand user with passwordless sudo
RUN useradd -m -s /bin/bash quicksand
COPY quicksand-sudoers /etc/sudoers.d/quicksand
# Rust agent via multi-stage build
FROM rust:bookworm AS agent-builder
WORKDIR /build
COPY agent/Cargo.toml agent/Cargo.lock* ./
COPY agent/src ./src
RUN cargo build --release && strip /build/target/release/quicksand-guest-agent
# In final stage:
COPY --from=agent-builder /build/target/release/quicksand-guest-agent /usr/local/bin/
# DHCP networking + CMD
CMD ["/sbin/init"]
For Alpine, use rust:alpine with musl-dev for static linking.
Reference: packages/contrib/quicksand-ubuntu/quicksand_ubuntu/__init__.py
Must export:
image — _ImageProvider instance (registered as quicksand.images entry point){Distro}Sandbox — Sandbox subclass with default image kwarg[project]
name = "quicksand-{distro}"
version = "0.1.0"
dependencies = ["quicksand-core"]
[build-system]
requires = ["hatchling", "quicksand-image-tools"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["quicksand_{distro}"]
artifacts = [
"quicksand_{distro}/images/*.qcow2",
"quicksand_{distro}/images/*.kernel",
"quicksand_{distro}/images/*.initrd",
"quicksand_{distro}/docker/*",
]
[tool.hatch.build.targets.wheel.hooks.custom]
[project.entry-points."quicksand.images"]
{distro} = "quicksand_{distro}:image"
Reference: packages/contrib/quicksand-ubuntu/hatch_build.py
The build hook detects architecture, sets platform-specific wheel tags, and builds the Docker image if the qcow2 doesn't exist yet.
The release pipeline automatically discovers base image packages via provider.type = "base" on the quicksand.images entry point. No manual registration needed.
uv build --package quicksand-{distro}
from quicksand_{distro} import {Distro}Sandbox
async with {Distro}Sandbox() as sb:
result = await sb.execute("cat /etc/os-release")
print(result.stdout)
Take microsoft/new-base-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 apt.
Without those the skill loads but fails at the first command.