Commit e8e953dad408
Changed files (1)
alpine
alpine/README.md
@@ -0,0 +1,84 @@
+# Alpine Diskless Appliances
+
+Alpine Linux diskless appliances for Raspberry Pi 4 homelab servers. The entire
+OS runs from an initramfs in RAM — the SD card is read-only after boot.
+
+## Hosts
+
+| Host | Role | Packages |
+|------|------|----------|
+| **athena** | DNS (BIND), WireGuard | `bind`, `wireguard-tools` |
+| **demeter** | DNS, MQTT | `bind`, `mosquitto`, `wireguard-tools` |
+| **aix** | File/backup server | `wireguard-tools` |
+
+## Architecture
+
+```
+alpine/
+├── common/ # Shared across all hosts
+│ ├── packages # Base package list
+│ ├── setup.sh # Common OpenRC setup (runlevels, users)
+│ └── root/ # Root filesystem skeleton (sshd, chrony, etc.)
+├── hosts/<hostname>/ # Per-host overrides
+│ ├── packages # Additional packages
+│ ├── setup.sh # Host-specific services
+│ └── root/ # Host-specific config files
+├── generated/ # Nix-generated configs (gitignored)
+│ ├── zones/ # DNS zone files from globals.nix
+│ └── wireguard/ # WireGuard configs per host
+├── images/ # Build output (gitignored)
+├── build.sh # Build initramfs in container (aarch64)
+├── build-inner.sh # Runs inside the Alpine container
+├── deploy.sh # rsync update to running host
+├── flash.sh # Flash SD card (partition + copy)
+└── test.sh # QEMU testing
+```
+
+## Usage
+
+All commands via the top-level `Makefile`:
+
+```bash
+# Generate DNS zones + WireGuard configs from Nix expressions
+make alpine/generate
+
+# Build a host image (runs generate first)
+make alpine/build/athena
+
+# Build all hosts
+make alpine/all
+
+# Test in QEMU
+make alpine/test/athena
+make alpine/validate/athena
+make alpine/stop/athena
+
+# Deploy update to running host (rsync, needs reboot)
+make alpine/deploy/athena
+
+# Flash a new SD card
+make alpine/flash/athena DEVICE=/dev/sda
+```
+
+## How It Works
+
+1. **Generate** — Nix evaluates `globals.nix` to produce DNS zone files and
+ WireGuard configs (symlinked into `generated/`)
+2. **Build** — A container (podman/docker) runs Alpine aarch64 via QEMU
+ user-mode, installs packages into a rootfs, runs setup scripts, then packs
+ everything into a cpio+gzip initramfs
+3. **Boot** — RPi firmware loads U-Boot → kernel + initramfs → entire OS in RAM
+4. **Persist** — A small ext4 partition (`label=persist`) holds SSH host keys
+ and WireGuard private keys, mounted at boot by the `persist-keys` init script
+
+## Secrets
+
+Secrets (`.age` files) are decrypted at boot by the `secrets` OpenRC init
+script using the host's SSH ed25519 key. Same encrypted files as the NixOS
+agenix setup, same keys.
+
+## Updating
+
+- Rebuild image: `make alpine/build/<host>`
+- Deploy: `make alpine/deploy/<host>` (rsync + reboot)
+- Rollback: previous initramfs saved as `initramfs.prev` on the boot partition