Building Custom Root FS as OCI Images¶
This page explains how to use the GitHub Actions workflow that builds custom WSL images and publishes them as OCI-compatible images to GitHub Container Registry (ghcr.io).
Overview¶
The build-rootfs-oci.yaml
workflow allows you to build custom WSL root
filesystems for different Linux distributions and push them as container images
to ghcr.io
. This makes it easy to distribute and version your custom WSL
images.
Triggering the Workflow¶
The workflow can be triggered in three ways:
1. Manual Trigger (Workflow Dispatch)¶
You can trigger it manually using GitHub's workflow dispatch feature to build a single flavor:
Via GitHub Web Interface¶
- Go to the "Actions" tab in your GitHub repository
- Select "Build and Push Custom Root FS as OCI Image" from the workflow list
- Click "Run workflow"
- Fill in the required parameter:
- Flavor: Choose from
ubuntu
,arch
,alpine
,debian
, oropensuse
- Flavor: Choose from
- Click "Run workflow"
Via GitHub CLI¶
Via REST API¶
curl -X POST \
-H "Authorization: token YOUR_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/OWNER/REPO/actions/workflows/build-rootfs-oci.yaml/dispatches \
-d '{"ref":"main","inputs":{"flavor":"arch"}}'
2. Automatic Trigger (Deploy Branch)¶
The workflow runs automatically when you push to the deploy/images
branch.
This triggers a build of all supported flavors with their default versions.
3. Scheduled Trigger¶
The workflow runs automatically every Sunday at 2:00 AM UTC to build fresh images with the latest updates for all flavors.
Version Requirements¶
The workflow enforces specific version formats for different flavors:
- Arch Linux: Must use format
YYYY.MM.DD
(e.g.,2025.08.01
) - Alpine: Must use format
X.Y.Z
(e.g.,3.22.1
) - Ubuntu, Debian, OpenSUSE: Must use
latest
Supported Flavors¶
The workflow supports the following Linux distributions with their respective sources:
- ubuntu: Ubuntu WSL Image from
https://cdimages.ubuntu.com/ubuntu-wsl/daily-live/current/questing-wsl-amd64.wsl
- arch: Arch Linux bootstrap image from
https://archive.archlinux.org/iso/VERSION/archlinux-bootstrap-VERSION-x86_64.tar.zst
- alpine: Alpine Linux minirootfs from
https://dl-cdn.alpinelinux.org/alpine/vX.Y/releases/x86_64/alpine-minirootfs-X.Y.Z-x86_64.tar.gz
- debian: Debian rootfs from
https://doi-janky.infosiftr.net/job/tianon/job/debuerreotype/job/amd64/lastSuccessfulBuild/artifact/stable/rootfs.tar.xz
- opensuse: OpenSUSE Tumbleweed from
https://download.opensuse.org/tumbleweed/appliances/opensuse-tumbleweed-dnf-image.x86_64-lxc-dnf.tar.xz
Build Process Overview¶
- Variable Setup: Uses gomplate to generate build matrix from template, determining flavors and versions based on trigger type
- Rootfs Download: Fetches appropriate base rootfs for each flavor from upstream sources
- Arch Special Handling: For Arch Linux, repackages bootstrap image to remove directory prefix
- OS Information Extraction: Dynamically extracts version and flavor information from the rootfs
- Base Image Creation: Creates unmodified base OCI image using Dockerfile template
- Environment Setup: Prepares chroot environment with mounted filesystems for customization
- Configuration: Applies customizations using provided scripts in chroot environment
- Configured Image Creation: Builds and pushes customized OCI image using Dockerfile template
- Metadata Generation: Creates JSON metadata files for both base and configured images
- Artifact Upload: Uploads metadata for collection (deploy/images branch only)
- Metadata Publishing: Collects and publishes metadata to rootfs branch (deploy/images branch only)
Architecture¶
The workflow consists of three main jobs:
1. set-variables¶
This job determines the build matrix based on the trigger type:
- Uses gomplate to generate the build matrix from the
builtins_matrix.json.tpl
template - For workflow dispatch: filters to build only the selected flavor
- For automatic triggers (deploy branch/scheduled): builds all supported flavors
- Outputs the matrix configuration for use by subsequent jobs
2. build-and-push-rootfs¶
This is the main job that runs for each flavor in the matrix and:
- Sets environment variables from the matrix (FLAVOR, VERSION, UPSTREAM_URL)
- Downloads the base filesystem from the upstream URL
- For Arch Linux: repackages the bootstrap image to remove the
root.x86_64
prefix - Extracts OS information from the rootfs to dynamically determine version and flavor
- Creates both base and configured OCI images:
- Base image (FLAVOR-base): Unmodified rootfs with root user
- Configured image (FLAVOR): Customized rootfs with regular user
- Uses gomplate templates to generate Dockerfiles for both image types
- Builds custom rootfs with chroot environment configuration
- Generates JSON metadata for both image variants
- Uploads artifacts for metadata collection
3. collect-rootfs-metadata & publish-rootfs-metadata¶
These jobs run only on the deploy/images
branch and:
- collect-rootfs-metadata: Downloads all JSON metadata artifacts and combines them
- publish-rootfs-metadata: Commits the collected metadata to the
rootfs
branch asbuiltins.rootfs.json
Output¶
The workflow produces OCI-compatible container images pushed to GitHub Container Registry. For each flavor, two images are created:
Base Images (Unmodified):¶
Configured Images (Customized):¶
For example:
ghcr.io/antoinemartin/powershell-wsl-manager/arch-base:2025.08.01
ghcr.io/antoinemartin/powershell-wsl-manager/arch:2025.08.01
ghcr.io/antoinemartin/powershell-wsl-manager/alpine-base:3.22.1
ghcr.io/antoinemartin/powershell-wsl-manager/alpine:3.22.1
ghcr.io/antoinemartin/powershell-wsl-manager/ubuntu-base:latest
ghcr.io/antoinemartin/powershell-wsl-manager/ubuntu:latest
Using the Images¶
Once built, these OCI images can be:
-
Used directly with Wsl-Manager by referencing the Docker URI:
-
Used as builtin images:
-
Pulled and converted back to WSL rootfs:
-
Used directly in container environments that support OCI images
-
Referenced in other workflows as base images for further customization
Customization¶
The workflow includes comprehensive customization logic that creates two types of images:
Base Images¶
- Contains the unmodified rootfs from upstream sources
- Uses root user (UID 0)
- No custom configuration applied
- Tagged as
FLAVOR-base
Configured Images¶
- Downloads base rootfs from official distribution sources
- Mounts necessary filesystems (
/dev
,/proc
,/sys
) for chroot environment - Installs configuration files:
p10k.zsh
- Powerlevel10k Zsh theme configurationconfigure.sh
- Distribution-specific setup script
- Runs configuration in a chroot environment as root
- Cleans up temporary files and unmounts filesystems
- Creates optimized WSL rootfs with all customizations applied
- Uses regular user with the flavor name as username (UID 1000)
- Tagged as
FLAVOR
The customization process ensures that each configured flavor gets:
- A minimal, optimized WSL environment
- Consistent shell configuration across all distributions
- Distribution-specific optimizations and package installations
- Proper user setup for WSL usage
Permissions¶
The workflow requires the following permissions:
contents: read
- to checkout the repositorypackages: write
- to push images to ghcr.iocontents: write
- to commit metadata to the rootfs branch (deploy/images branch only)
Make sure your repository has the necessary permissions configured for GitHub Packages.
Dynamic Version Detection¶
The workflow includes intelligent version detection that:
- Extracts OS information from the downloaded rootfs using
os-release
files - Uses extracted
VERSION_ID
orIMAGE_VERSION
to override default versions when available - Uses extracted
ID
to override the flavor name when available - Falls back to matrix-defined versions when OS information is not available
- Outputs all extracted variables for debugging and transparency
This ensures that the built images use the actual version information from the rootfs rather than potentially outdated defaults.
Template System¶
The workflow uses gomplate templates to generate configuration files dynamically:
Matrix Template (builtins_matrix.json.tpl
)¶
- Defines supported flavors with their versions and upstream URLs
- Uses environment variables for default versions (
ARCH_DEFAULT_VERSION
,ALPINE_DEFAULT_VERSION
) - Generates the build matrix consumed by the workflow
Dockerfile Template (Builtin.dockerfile.tpl
)¶
- Creates Dockerfiles for both base and configured images
- Uses variables like
WSL_UID
,WSL_USERNAME
,WSL_CONFIGURED
,WSL_TYPE
- Allows consistent Dockerfile generation across all flavors
Metadata Template (rootfs.json.tpl
)¶
- Generates JSON metadata describing each built image
- Includes information about user configuration, image type, and Docker references
- Used for builtin image resolution in Wsl-Manager
This template-based approach ensures consistency and maintainability across all supported distributions.