This guide builds the bootable dstack guest-OS release artifacts from source.
It is for OS developers, release maintainers, and operators who want a custom
image. You do not need to build an image for normal self-hosted onboarding:
dstackup install downloads and verifies a published guest-OS release by
default.
Important
mkosi is the default and recommended OS backend. The Yocto backend under
os/yocto/ is deprecated. It remains only so existing Yocto images can
still be rebuilt. Do not use it for new images, new features, or release
work. See Deprecated: Yocto backend.
The mkosi build still reads some patches, units, and scripts from
os/yocto/, so that directory must not be deleted yet; see
../os/README.md.
The default prod build produces:
- a bare-metal/CVM bundle for Intel TDX and, when the SEV firmware artifact is available, AMD SEV-SNP;
- a UKI disk-image bundle for the GCP confidential-VM boot path;
- dm-verity rootfs data, launch-measurement material, checksums, and the unified
digest.txtOS identity.
The mkosi backend lives in os/mkosi/. Backend-independent rootfs payload,
artifact contract, measurement, and release packaging live outside the backend;
see ../os/README.md. Backend internals, the
reproducibility model, and the component cache are described in
../os/mkosi/README.md.
Use an x86-64 Linux host with:
- Git;
- Docker Engine, usable by the current user, that can run
--privilegedcontainers (mkosi needs loop devices, device-mapper, and mounts); - outbound HTTPS access for the pinned Debian snapshot, upstream source archives, and Rust crates;
- tens of gigabytes of free disk space on a regular filesystem (not overlayfs) for the build directory.
No Rust, Go, C, or C++ toolchain is needed on the host; every compiler is pinned and runs inside mkosi's build overlay.
TEE hardware is not required to build the image. It is required only when you boot and attest the resulting image on the corresponding platform.
Check the basics before starting:
docker version
git --version
df -h .From a fresh checkout:
git clone https://github.com/Dstack-TEE/dstack.git
cd dstack
make os-imagemake os-image runs one complete, cold production image build in a container
that pins mkosi and the host tools it drives. It needs no submodules and is
equivalent to:
./os/mkosi/repro-build/repro-build.shThe build refuses to run from a dirty worktree, because the recorded source
revision would not describe the compiled sources. Commit or stash changes
first. (Native os/mkosi/build.sh builds also accept DSTACK_ALLOW_DIRTY=1,
which records the revision as -modified; the containerized build does not
forward it.)
A cold production build takes roughly 30–45 minutes on a 16-job host, depending mostly on network speed.
Release artifacts are written under:
os/mkosi/repro-build/build/out/prod/
├── dstack-<version>/
├── dstack-<version>.tar.gz
├── dstack-<version>-uki.tar.gz
└── dstack-<version>-kernel-devel.tar.gz
To use a different build directory, run
./os/mkosi/repro-build/repro-build.sh -o DIR. When the build finishes it
prints the os_image_hash and the SHA-256 of both archives.
The bare-metal archive includes the kernel, initramfs, OVMF firmware,
partitioned dm-verity rootfs, platform measurement CBOR files,
sha256sum.txt, digest.txt, and metadata.json. The UKI archive
includes the bootable disk.raw plus its identity and measurement files. The
kernel-devel archive is the build tree for out-of-tree modules; it is not part
of the image identity (see below).
Inspect and verify an archive with:
mkdir -p /tmp/dstack-image
tar -xzf os/mkosi/repro-build/build/out/prod/dstack-<version>.tar.gz \
-C /tmp/dstack-image
cd /tmp/dstack-image/dstack-<version>
sha256sum -c sha256sum.txt
test "$(sha256sum sha256sum.txt | awk '{print $1}')" = "$(cat digest.txt)"Production is the default. To build both variants once:
FLAVORS="prod dev" ./os/mkosi/repro-build/repro-build.shThe development archive is written to out/dev/dstack-dev-<version>.tar.gz
and records "is_dev": true in metadata.json.
Each release publishes the guest kernel's build tree for building out-of-tree modules:
dstack-<version>-kernel-devel.tar.gz, the exported build tree, andghcr.io/dstack-tee/dstack-kernel-builder:<version>, the same tree plus a compiler from the pinned Debian snapshot the kernel was built with, withKDIRset.
FROM ghcr.io/dstack-tee/dstack-kernel-builder:<version> AS kmod
COPY mymod/ /src
RUN make -C $KDIR M=/src modules
FROM alpine
COPY --from=kmod /src/mymod.ko /opt/To build the image locally from a release archive, run
os/image/kernel-builder/build.sh <kernel-devel.tar.gz> <image-ref>.
Neither artifact is part of sha256sum.txt, so they do not affect
os_image_hash. A module loaded by an application (which needs
CAP_SYS_MODULE) is covered by the container image digest and therefore the
compose hash, but it is not measured separately and dstack does not enforce
module signatures.
For a release candidate:
make os-repro-checkThis performs two cold production builds in different build paths and with
different job counts, then compares both release archives byte for byte. It
takes roughly twice the resources of a single build. The first leg's artifacts
remain under os/mkosi/repro-build/build/a/.
The containerized build is the recommended release path. On a host with the
pinned mkosi version (see MKOSI_VERSION in os/mkosi/versions.env) and the
packages reported by mkosi --directory os/mkosi dependencies, and with root
privileges (or a working user namespace), the backend can be driven directly:
./os/mkosi/build.sh lint # static contract, seconds
./os/mkosi/build.sh image "$PWD/os/mkosi/build" # cached iteration build
./os/mkosi/build.sh --no-cache image "$PWD/os/mkosi/build" # cold build
./os/build.sh --flavors "prod dev" --build-dir "$PWD/os/mkosi/build"A native image build reuses a component-output cache by default and skips the
release tarballs; disk.raw, the measurements, and metadata.json are still
produced. Pass --archive to get the tarballs from a cached build, or
--no-cache for a release-equivalent cold build. See
../os/mkosi/README.md for cache details.
The generic entrypoint os/build.sh dispatches to os/<backend>/build.sh and
defaults to mkosi.
Ensure docker version works as the same non-root user that owns the
checkout. The build container runs privileged and hands ownership of the build
directory back to the calling user when it exits.
mkosi assembles its build root as an overlayfs, which cannot be stacked on
another overlayfs. Choose a build directory on a regular filesystem with
./os/mkosi/repro-build/repro-build.sh -o DIR.
Confirm outbound network and DNS access, then rerun the same command. Package downloads come from an immutable Debian snapshot, so a retry fetches identical content.
The disposable build directories are:
os/mkosi/repro-build/build/
os/mkosi/build/
~/.cache/dstack/mkosi-dev/ # native component cache
They are ignored by Git (or live outside the checkout) and can be removed when no build is running. Copy the release archives elsewhere first if you need them.
Warning
The Yocto backend is deprecated and will be removed. Do not use it for new
images or releases, and do not add features to it. Every build entrypoint
prints a deprecation warning. Files under os/yocto/ that the mkosi build
still reads remain live inputs of the default image until they are moved.
The Yocto backend is kept only so existing Yocto-built images can still be rebuilt and verified. Its entrypoints have moved to explicitly named targets:
| Deprecated command | Replacement |
|---|---|
make os-image-yocto |
make os-image |
make os-repro-check-yocto |
make os-repro-check |
make os-yocto / ./os/build.sh --backend yocto |
make os / ./os/build.sh |
make os-deps |
not needed; mkosi uses no submodules |
make os-image-yocto initializes the eight Yocto dependency submodules and runs
os/yocto/repro-build/repro-build.sh -n in its pinned Ubuntu builder container.
Archives are written to os/yocto/repro-build/dist/; use
RELEASE_FLAVORS="prod dev" to build both flavors. See
../os/yocto/README.md for the remaining details.