Files
2026-09-04 15:48:04 +02:00

8.5 KiB
Raw Permalink Blame History

Testing NaxOS on macOS

This guide provides step-by-step instructions for testing and running the complete NaxOS appliance on macOS. It covers full live virtual machine testing with QEMU, rapid local frontend/API development, and headless automated verification.


Testing Methods Overview

Method Target Use Case Requirements
Method 1: Full Live QEMU VM Complete appliance test: Linux 6.18 kernel, real OpenZFS pool tank, live naxos-api daemon, and naxos-ui web interface. qemu-system-x86_64 (Homebrew or Nix)
Method 2: Local UI / API Dev Rapid UI development, styling iteration, and component testing on macOS without a VM. Node.js 20+, npm
Method 3: Headless Verification Automated end-to-end browser capture and visual validation. Python 3 + Playwright or Chromium

Method 1: Full Appliance VM Testing via QEMU

This method boots the actual NaxOS NixOS 26.05 operating system with Linux kernel 6.18, creates a virtual target disk, provisions a real OpenZFS pool (tank), and runs the full API daemon and Web UI with port forwarding to macOS.

1. Prerequisites

Install QEMU on macOS using Homebrew (or via Nix):

# Using Homebrew
brew install qemu

# Or if you use Nix on macOS:
nix-shell -p qemu

Verify that qemu-system-x86_64 is available:

qemu-system-x86_64 --version

Note

Apple Silicon (M1/M2/M3/M4) Note: When running qemu-system-x86_64 on Apple Silicon Macs, QEMU uses TCG (Tiny Code Generator) software emulation. Because it is emulating x86_64 on ARM64, boot times will take approximately 1530 seconds.


2. Download the NaxOS Installation Media

Download the official bootable ISO from the Gitea package registry:

curl -fSL -o /tmp/naxos-installer.iso \
  "https://git.lholz.de/api/packages/naxos/generic/naxos-installer-iso/1.0.0/naxos-installer-x86_64-linux.iso"

3. Prepare Boot Artifacts & Virtual Drive

Extract the direct-boot kernel and initrd from the ISO (or boot directly from the ISO cdrom):

# Create a 20 GB QCOW2 virtual drive for OpenZFS testing
qemu-img create -f qcow2 /tmp/naxos-target-disk.qcow2 20G

# Mount or extract the ISO to retrieve the kernel and initrd
mkdir -p /tmp/naxos-iso-mnt /tmp/naxos-boot
hdiutil attach -nobrowse -readonly -mountpoint /tmp/naxos-iso-mnt /tmp/naxos-installer.iso
cp -R /tmp/naxos-iso-mnt/boot /tmp/naxos-boot/
hdiutil detach /tmp/naxos-iso-mnt

Find the extracted kernel and initrd paths:

KERNEL=$(find /tmp/naxos-boot -name bzImage | head -n 1)
INITRD=$(find /tmp/naxos-boot -name initrd | head -n 1)
echo "Kernel: $KERNEL"
echo "Initrd: $INITRD"

4. Launch the NaxOS Virtual Appliance

Start QEMU with:

  • RAM: 2048 MB (2 GB)
  • CPU: 2 cores (-smp 2)
  • Storage: VirtIO disk attached to /tmp/naxos-target-disk.qcow2 (appears as /dev/vda in the VM)
  • Networking: User-mode networking with port forwarding:
    • Host port 18080 -> VM port 80 (NaxOS Web UI)
    • Host port 18443 -> VM port 8443 (NaxOS HTTPS / API)
  • Serial Console: Stdio redirected to your terminal for real-time kernel and shell access.
qemu-system-x86_64 \
  -m 2048 \
  -smp 2 \
  -kernel "$KERNEL" \
  -initrd "$INITRD" \
  -append "init=/nix/store/*-nixos-system-nixos-*/init console=ttyS0,115200n8 loglevel=6" \
  -drive file=/tmp/naxos-target-disk.qcow2,if=virtio,format=qcow2 \
  -net nic,model=virtio \
  -net user,hostfwd=tcp::18443-:8443,hostfwd=tcp::18080-:80 \
  -nographic

Once booted, press Enter to get the root prompt:

<<< Welcome to NixOS 26.05! >>>

[root@nixos:~]#

5. Initialize the OpenZFS Storage Pool in the VM

Inside the VM terminal, create the real OpenZFS pool tank on the virtual drive /dev/vda:

# Create OpenZFS pool
zpool create tank /dev/vda -f

# Create standard appliance datasets
zfs create tank/media
zfs create tank/backup
zfs create tank/container

# Verify pool health and status
zpool status
zpool list
zfs list

You should see:

NAME             SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
tank            19.5G   832K  19.5G        -         -     0%     0%  1.00x    ONLINE  -

6. Verify Real System Metrics & Declarative Rebuild

The NaxOS Management Daemon (naxos-api) queries real kernel interfaces rather than mocked data:

  • ZFS ARC Cache: /proc/spl/kstat/zfs/arcstats
  • Uptime: /proc/uptime
  • Memory: os.totalmem() and os.freemem()
  • Disk I/O: /proc/diskstats
  • Journal Logs: Real journalctl -n 100 -o json --no-pager

Test the API daemon endpoints from inside the VM or from macOS:

# Check system metrics from your Mac:
curl http://localhost:18443/api/v1/system/metrics

# Check real ZFS pools:
curl http://localhost:18443/api/v1/storage/pools

# Check active system services:
curl http://localhost:18443/api/v1/system/services

Testing "Apply Changes" (Live SSE Stream)

Trigger a declarative system rebuild from macOS:

curl -N -X POST http://localhost:18443/api/v1/system/rebuild

You will receive real Server-Sent Events (SSE) streaming live steps:

  1. validating - Synthesizing declarative NixOS configuration.
  2. building - Executing /run/current-system/sw/bin/nixos-rebuild dry-activate.
  3. reloading - Applying dataset quotas and reloading Samba shares.
  4. completed - Generation activated successfully.

7. Accessing the Web Dashboard

Open your web browser on macOS and navigate to:

http://localhost:18080

You will see:

  1. Glassmorphism Appliance Dashboard: Live storage capacity, real ARC hit ratio (100%), ARC cache size, CPU and memory gauges.
  2. Storage Management: Visual hierarchy of pool tank, datasets (media, backup, container), and snapshots.
  3. App Store: Curated 1-click catalog (Immich, Jellyfin, Nextcloud, Vaultwarden).
  4. Apply Changes Modal: Interactive rebuild viewer streaming real systemd activation logs.

8. Clean VM Shutdown

To cleanly unmount OpenZFS datasets, export the storage pool, and power down the virtual machine, execute inside the VM console:

poweroff

Or press Ctrl+A then X to exit QEMU immediately.


Method 2: Local UI & API Rapid Development

To develop or preview the Web UI directly on macOS without launching QEMU:

1. Build and Run the Web UI

cd naxos-ui

# Install dependencies
npm install

# Start Vite development server
npm run dev

Open http://localhost:5173 in your browser.

2. Previewing the Production SPA Build

cd naxos-ui
npm run build
npm run preview -- --port 3000

Open http://localhost:3000 to inspect the production asset bundle.

3. Running the Management Daemon Locally (Mock Fallback)

If running naxos-api on macOS outside of Linux, it automatically falls back gracefully when Linux-specific paths (/proc/spl/kstat/zfs/arcstats, zpool) are unavailable:

cd naxos-api
npm install
npm run build
npm start

The API daemon listens on http://localhost:8088.


Method 3: Headless Automated Browser Verification

To run headless end-to-end tests or re-generate screenshots automatically:

# Using Python Playwright
python3 -m pip install playwright
python3 -m playwright install chromium

# Run headless screenshot verification
python3 - << 'EOF'
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1440, "height": 900})
    page.goto("http://localhost:18080")
    page.wait_for_selector("text=System Healthy")
    page.screenshot(path="test-overview.png")
    print("Screenshot captured successfully!")
    browser.close()
EOF

Troubleshooting & FAQ

Port 18080 or 18443 is already in use

Check which process is listening on the port:

lsof -i :18080
lsof -i :18443

Kill the stale process or change the -net user,hostfwd=tcp::18080-:80 parameter in the QEMU launch command to an alternate port (e.g. hostfwd=tcp::28080-:80).

Strict MIME Type Errors (Failed to load module script)

Browsers enforce strict MIME checking for ES modules (Content-Type: text/javascript; charset=utf-8). If serving files via Python or custom static servers, ensure MIME types are explicitly configured:

import mimetypes
mimetypes.add_type('application/javascript', '.js')
mimetypes.add_type('text/css', '.css')

The standard naxos-api and production nginx containers already include appropriate MIME headers out of the box.