Modern WSL Distribution Packages: .wsl Files, OOBE, and Manifests
Package modern WSL distributions as .wsl files with deterministic OOBE, manifests, Terminal profiles, integrity checks, and clean install acceptance.
Modern WSL distribution publishing has two related artifacts: a Linux root filesystem packaged as a tar archive with a .wsl extension, and a distribution manifest that makes named releases discoverable through wsl --install. This is different from importing a one-off tar into a local machine. A package needs a first-run experience, stable registration metadata, and an install path users can repeat without hand-editing the imported filesystem.
Microsoft’s current custom-distribution guide applies to WSL release 2.4.4 and later. Verify the installed WSL package before testing the workflow; a Windows build number alone does not prove that the Store-delivered WSL package supports this packaging path. Microsoft documents wsl --version and wsl --update as the way to check and update the WSL package where available.
The packaging commands and onboarding hooks below target Windows and WSL. They cannot be executed on the macOS authoring host; test a release candidate in a clean Windows account or VM with the minimum supported WSL package.
Separate package identity from per-user configuration
The root filesystem should include two different configuration files. /etc/wsl-distribution.conf is maintained by the distro publisher and describes install/first-run behavior, such as OOBE, shortcuts, and Windows Terminal profile generation. /etc/wsl.conf configures local behavior for an installed distribution, such as its default user or systemd setting. Do not use one file as a replacement for the other, and keep any per-user state out of a reusable base image.
Microsoft recommends that both files be owned by root:root and mode 0644. The published recommendations also say to include a root account with UID 0 in /etc/passwd, omit /etc/resolv.conf from the root filesystem, and not bundle a kernel or initramfs in the distro tar. Those are packaging checks, not optional runtime tuning: a rootfs with host-specific resolver state or a bundled kernel can make otherwise identical installs behave differently across machines.
The distribution config can declare an OOBE command, default UID/name, a shortcut icon, and a Windows Terminal profile template. The separate wsl.conf file is where the image maintainer decides whether systemd is enabled by default and configures supported distro-local behavior. Preserve this division when generating an image: publisher policy is part of the package, while local WSL settings remain subject to the installed user’s environment and product version.
Design OOBE as a one-time, repeatable transaction
oobe.command runs the first time the user opens an interactive shell in the distribution. Microsoft documents that a nonzero return is treated as an unsuccessful first-run action and prevents the user from opening a shell. The OOBE script is therefore part of the installation gate, not a best-effort welcome banner.
Use a short script that is safe to retry and can distinguish “user already exists” from a real failure. A simplified Debian/Ubuntu-style example follows the published sample’s UID model:
#!/usr/bin/env bash
set -euo pipefail
default_uid=1000
default_groups='adm,cdrom,sudo,dip,plugdev'
if getent passwd "$default_uid" >/dev/null; then
exit 0
fi
while true; do
read -r -p 'New Linux username: ' username
if [[ "$username" =~ ^[a-z_][a-z0-9_-]*$ ]]; then
if /usr/sbin/adduser --uid "$default_uid" --quiet \
--gecos '' "$username"; then
if /usr/sbin/usermod "$username" -aG "$default_groups"; then
exit 0
fi
/usr/sbin/deluser "$username"
fi
fi
printf 'Could not create that user; try again.\n' >&2
done
This example assumes the target image provides Debian/Ubuntu-style adduser, usermod, and deluser; it is not portable to every distribution. A different base OS needs a different account-management implementation. Set oobe.defaultUid to the UID the script creates so WSL’s selected default user agrees with /etc/passwd. Keep the script idempotent: if the account already exists, return success without creating a second identity or repeating destructive configuration.
Do not make first shell startup depend on an online package mirror, mutable network resource, or interactive prompt that can hang indefinitely. Preinstall required packages in the image build, give OOBE bounded retries and a clear failure message, and test its nonzero path intentionally. Because a failure blocks the shell, package acceptance should test both fresh first launch and an interrupted/retried onboarding run.
Create and validate the .wsl archive
Microsoft describes the .wsl file as a tar archive containing the root filesystem and WSL configuration files. The filesystem root must be the archive root; do not place the rootfs inside an extra parent directory. The guide recommends gzip compression and warns that other compression formats can reduce compatibility with older WSL versions. Build the tar in a Linux environment that preserves numeric ownership and modes, then rename the completed tar to .wsl.
For example, after assembling and validating a staged rootfs tree in a controlled Linux build environment:
tar --numeric-owner --absolute-names -C rootfs -c . | gzip --best \
> acme-dev.tar.gz
sha256sum acme-dev.tar.gz
mv acme-dev.tar.gz acme-dev.wsl
The command assumes GNU tar and gzip and must run in the build environment chosen by the publisher. Do not generate the archive with a Windows tool that silently loses Linux ownership, symlinks, modes, or case-sensitive names. Hash the final .wsl artifact as well as the source rootfs and record the toolchain and input package snapshot used to build it.
Install a local artifact with the documented file-based command, or double-click the .wsl file in File Explorer:
wsl.exe --version
wsl.exe --install --from-file D:\Artifacts\Acme-Dev.wsl
wsl.exe --list --verbose
The distribution config’s oobe.defaultName is required for the documented double-click experience. The user can provide an explicit registration name with the install command’s --name option where supported. Keep the artifact name, registered distro name, and manifest release name distinct in operational documentation; they are related identifiers, not necessarily the same string.
Publish the install manifest as a versioned index
The distribution manifest maps a flavor (the friendly channel name users type) to one or more installable version entries. Microsoft’s example includes a version Name, FriendlyName, a Default flag, and architecture-specific download information with a URL and SHA-256 hash. Users can install a flavor to select its default entry or name a specific version explicitly.
{
"ModernDistributions": {
"acme-dev": [
{
"Name": "acme-dev-2026.10",
"FriendlyName": "Acme Dev 2026.10",
"Default": true,
"Amd64Url": {
"Url": "https://downloads.example.invalid/acme-dev-2026.10.wsl",
"Sha256": "0xREPLACE_WITH_64_HEX_DIGEST"
},
"Arm64Url": {
"Url": "https://downloads.example.invalid/acme-dev-2026.10-arm64.wsl",
"Sha256": "0xREPLACE_WITH_64_HEX_DIGEST"
}
}
]
}
}
The .invalid hostnames and digest strings are placeholders; they are not downloadable artifacts. Replace each with a stable release URL and the exact SHA-256 of the corresponding architecture-specific package. Test the JSON with a parser, download each artifact through the same path users will use, recompute its hash, and confirm the registered distribution architecture matches the downloaded image. Do not point a “stable” manifest entry at an unversioned mutable tarball while keeping an old hash.
Microsoft documents two distribution-list routes: publishing through the WSL repository’s distribution manifest for the broad wsl --list --online catalog, or overriding the manifest URL for an enterprise/business group. The catalog route has membership criteria and maintainer review. Enterprise overrides are device configuration that affects WSL installation discovery; use a controlled test machine and document how to restore the official manifest. Do not change machine-wide registry values on a developer workstation as an ad hoc release test.
Generate a helpful but deterministic Windows Terminal profile
When a WSL distribution is installed, WSL can create a Windows Terminal profile. The publisher can provide a profile template in /etc/wsl-distribution.conf; WSL generates the profile and supplies its own name and command line, so the template should focus on supported presentation fields such as color schemes or font settings. Validate the JSON independently, then test the generated profile in the supported Windows Terminal version. A syntactically valid template can still reference a missing icon, color scheme, or font.
The start-menu shortcut is similarly explicit: shortcut.enabled controls whether it is created, and shortcut.icon must refer to an .ico file within Microsoft’s documented size limit. Include the asset in the rootfs at the exact path, verify that its casing matches, and test shortcut creation from a fresh install. These convenience surfaces should not be the only way to launch the distro; the registered name and wsl.exe command remain essential for automation and support.
Validate the full install, not just the archive
Use a clean Windows user profile or disposable VM with the minimum WSL release supported by the package. Install once from the .wsl file, then test first interactive launch, OOBE completion, default UID, root access, generated shortcut, Terminal profile, wsl --list --verbose, and explicit command invocation. Repeat with an interrupted OOBE, a user-selected install name, an unsupported architecture artifact, and a failed download/hash check.
For manifest distribution, verify both the default flavor and every pinned version name. Confirm the architecture URL and digest correspond to the intended build, and test the exact wsl --install invocation documented for the publishing route. After install, record the WSL package version, Windows build, distro release, registered name, rootfs build ID, and whether first-run onboarding came from the package or a local override.
The release is ready when the same immutable artifact installs reproducibly, onboarding either completes successfully or fails with an actionable recovery path, and the manifest resolves to a verifiable package for each architecture. A .wsl file is not just a renamed tar: its OOBE and metadata determine the user’s first usable shell, its manifest controls discoverability, and its hashes connect the published name to the bits actually installed.
Related:
- How WSL Actually Packages and Distributes Linux Distros
- How to Build and Register a Custom WSL Distribution from a Root Filesystem
Sources: