Skip to content

Build NixOS ISO

Build NixOS ISO #22

# Build NixOS ISO for Lenovo IdeaPad Slim 5x (Qualcomm Snapdragon X Plus x1p42100)
#
# This workflow builds natively on GitHub's arm64 hosted runners. The flake
# targets aarch64-linux, and the runner IS an ARM64 VM, so no QEMU emulation
# or cross-compilation is needed.
#
# Available ARM64 runners (GitHub-hosted):
# - ubuntu-24.04-arm (public repos: 4 vCPU / 16 GB RAM / 14 GB SSD)
# - ubuntu-22.04-arm (public repos: 4 vCPU / 16 GB RAM / 14 GB SSD)
# - ubuntu-24.04-arm (private repos: 2 vCPU / 8 GB RAM / 14 GB SSD)
# - ubuntu-22.04-arm (private repos: 2 vCPU / 8 GB RAM / 14 GB SSD)
#
# DISK SPACE WARNING:
# Standard ARM runners have ~14 GB SSD. A NixOS ISO build (kernel + full
# nixpkgs closure + squashfs) can exceed this. For reliable ISO builds,
# use a self-hosted runner on an actual ARM64 machine (e.g., Oracle Cloud
# free tier Ampere A1 instance).
name: Build NixOS ISO
on:
# Lightweight system closure builds on every push
push:
branches: ["dev"]
# Full ISO build only when manually triggered (saves runner minutes)
workflow_dispatch:
# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
############################################################################
# FULL ISO BUILD (native ARM64) — manual trigger only
############################################################################
build-iso:
name: Build ISO (native ARM64)
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04-arm
timeout-minutes: 360
steps:
- name: Check out repository
uses: actions/checkout@v4
# ------------------------------------------------------------------
# Nix Installation
# ------------------------------------------------------------------
- name: Install Nix
uses: cachix/install-nix-action@v30
with:
extra_nix_config: |
keep-derivations = false
keep-outputs = false
auto-optimise-store = true
cores = 2
max-jobs = 2
# ------------------------------------------------------------------
# Binary Caching (Cachix)
# ------------------------------------------------------------------
- name: Set up Cachix
uses: cachix/cachix-action@v15
with:
name: qcom-x1p42100
authToken: ${{ secrets.CACHIX_AUTH_TOKEN || '' }}
skipPush: ${{ secrets.CACHIX_AUTH_TOKEN == '' }}
# ------------------------------------------------------------------
# Aggressive disk space cleanup before building
# ------------------------------------------------------------------
- name: Free disk space (before and after Nix)
run: |
echo "=== Initial disk usage ==="
df -h /
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/hostedtoolcache 2>/dev/null || true
sudo apt-get clean && sudo apt-get autoremove -y 2>/dev/null || true
docker system prune -af --volumes 2>/dev/null || true
echo "=== After system cleanup ==="
df -h /
# ------------------------------------------------------------------
# Garbage collect Nix store
# ------------------------------------------------------------------
- name: Garbage collect Nix store
run: |
nix-collect-garbage -d || true
nix store gc --verbose || true
df -h /
# ------------------------------------------------------------------
# Build and Hardlink ISO
# ------------------------------------------------------------------
- name: Build NixOS ISO image
env:
NIX_BUILD_CORES: "2"
run: |
set -e
# Background GC monitor
(
while true; do
sleep 60
available=$(df / | tail -1 | awk '{print $4}')
if [ "$available" -lt 2097152 ]; then
echo "Low disk (<2GB), collecting garbage..."
nix-collect-garbage -d || true
fi
sleep 60
done
) &
BG_PID=$!
trap "kill $BG_PID 2>/dev/null || true" EXIT
nix build \
--extra-experimental-features 'nix-command flakes' \
--print-build-logs \
--show-trace \
--max-jobs 2 \
--cores 2 \
.#nixosConfigurations.slim5xISO.config.system.build.isoImage
kill $BG_PID 2>/dev/null || true
mkdir -p iso-output
# Copy ISO to output directory (avoids hard link permission issues)
if [ -d result/iso ]; then
cp result/iso/*.iso iso-output/
else
cp result/*.iso iso-output/
fi
ls -lh iso-output/
# ------------------------------------------------------------------
# Upload the result
# ------------------------------------------------------------------
- name: Upload ISO artifact
uses: actions/upload-artifact@v4
with:
name: nixos-iso-arm64
path: iso-output/*.iso
if-no-files-found: error
retention-days: 30
############################################################################
# LIGHTWEIGHT: Build system closure on every push (no ISO image)
############################################################################
build-system:
name: Build system closure
runs-on: ubuntu-24.04-arm
timeout-minutes: 360
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
with:
extra_nix_config: |
keep-derivations = false
keep-outputs = false
auto-optimise-store = true
cores = 2
max-jobs = 2
- name: Set up Cachix
uses: cachix/cachix-action@v15
with:
name: qcom-x1p42100
authToken: ${{ secrets.CACHIX_AUTH_TOKEN || '' }}
skipPush: ${{ secrets.CACHIX_AUTH_TOKEN == '' }}
- name: Free disk space
run: |
echo "=== Initial disk usage ==="
df -h /
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/hostedtoolcache 2>/dev/null || true
sudo apt-get clean && sudo apt-get autoremove -y 2>/dev/null || true
docker system prune -af --volumes 2>/dev/null || true
echo "=== After system cleanup ==="
df -h /
- name: Garbage collect Nix store
run: |
nix-collect-garbage -d || true
nix store gc --verbose || true
df -h /
- name: Build system closure
env:
NIX_BUILD_CORES: "2"
run: |
set -e
# Background GC monitor
(
while true; do
sleep 60
available=$(df / | tail -1 | awk '{print $4}')
if [ "$available" -lt 2097152 ]; then
echo "Low disk (<2GB), collecting garbage..."
nix-collect-garbage -d || true
fi
sleep 60
done
) &
BG_PID=$!
trap "kill $BG_PID 2>/dev/null || true" EXIT
nix build \
--extra-experimental-features 'nix-command flakes' \
--print-build-logs \
--show-trace \
--max-jobs 2 \
--cores 2 \
.#nixosConfigurations.qcom-nixos.config.system.build.toplevel
kill $BG_PID 2>/dev/null || true