docs: add guide on testing NaxOS on macOS with QEMU and local development
Validate Documentation / lint-docs (push) Successful in 14s
Validate Documentation / lint-docs (push) Successful in 14s
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
# Using Homebrew
|
||||
brew install qemu
|
||||
|
||||
# Or if you use Nix on macOS:
|
||||
nix-shell -p qemu
|
||||
```
|
||||
|
||||
Verify that `qemu-system-x86_64` is available:
|
||||
```bash
|
||||
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 15–30 seconds.
|
||||
|
||||
---
|
||||
|
||||
### 2. Download the NaxOS Installation Media
|
||||
|
||||
Download the official bootable ISO from the Gitea package registry:
|
||||
|
||||
```bash
|
||||
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):
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```bash
|
||||
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.
|
||||
|
||||
```bash
|
||||
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:
|
||||
```text
|
||||
<<< 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`:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```text
|
||||
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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```bash
|
||||
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:
|
||||
```text
|
||||
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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```bash
|
||||
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:
|
||||
```python
|
||||
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.
|
||||
Reference in New Issue
Block a user