docs: add guide on testing NaxOS on macOS with QEMU and local development
Validate Documentation / lint-docs (push) Successful in 14s
@@ -62,6 +62,7 @@ Curated 1-click self-hosted application catalog with hardware-accelerated Immich
|
|||||||
|
|
||||||
### 2. User & Administrator Guides
|
### 2. User & Administrator Guides
|
||||||
- [Installation & Live ISO Guide](docs/guides/installation.md)
|
- [Installation & Live ISO Guide](docs/guides/installation.md)
|
||||||
|
- [Testing NaxOS on macOS (QEMU & Local)](docs/guides/testing-on-mac.md)
|
||||||
- [Migration Guide: Transitioning from `nixos-lukas` to NaxOS](docs/guides/migration-from-nixos-lukas.md)
|
- [Migration Guide: Transitioning from `nixos-lukas` to NaxOS](docs/guides/migration-from-nixos-lukas.md)
|
||||||
|
|
||||||
### 3. Developer Manual
|
### 3. Developer Manual
|
||||||
|
|||||||
@@ -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.
|
||||||
|
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 295 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 295 KiB |