From 9327335fb8e015f2337e1848eaa61a76e782862e Mon Sep 17 00:00:00 2001 From: Administrator Date: Sat, 21 Feb 2026 04:26:10 +0000 Subject: [PATCH] docs: update Netgrimoire/Pocket/Deployment_Guide --- Netgrimoire/Pocket/Deployment_Guide.md | 714 ++++++++++++++++++++++--- 1 file changed, 626 insertions(+), 88 deletions(-) diff --git a/Netgrimoire/Pocket/Deployment_Guide.md b/Netgrimoire/Pocket/Deployment_Guide.md index 5cc3ebb..4ccbe06 100644 --- a/Netgrimoire/Pocket/Deployment_Guide.md +++ b/Netgrimoire/Pocket/Deployment_Guide.md @@ -2,7 +2,7 @@ title: Pocket Grimoire description: published: true -date: 2026-02-20T04:54:31.450Z +date: 2026-02-21T04:25:58.904Z tags: editor: markdown dateCreated: 2026-02-20T04:41:35.122Z @@ -502,35 +502,33 @@ sudo apt install -y zfsutils-linux sudo zpool list ``` -### 4. Configure ZFS Pools +### 4. Initial Drive Setup on Netgrimoire (Before Moving to Pocket) -**Important:** Replace `/dev/sdX` with your actual device identifiers. Use `lsblk` to identify drives. +**IMPORTANT: Build drives on Netgrimoire first, then move to Pocket Grimoire.** -**Create Vault Pool (Always Connected):** +This approach allows you to: +- Create encrypted pools with proper passphrases +- Perform initial ZFS sync while drives are fast-connected (SATA/USB 3.0) +- Verify data integrity before moving drives +- Test encryption/unlock on powerful hardware first + +#### On Netgrimoire: Create and Populate Drives + +**Connect drives to Netgrimoire:** +- VAULT SSD (1-2TB) via USB 3.0 or SATA +- MEDIA-PERSONAL SSD (2TB+) via USB 3.0 or SATA +- MEDIA-FAMILY SSD (2TB+) via USB 3.0 or SATA (optional, can be created later) + +**Identify drives:** ```bash -# Identify Vault SSD +# On Netgrimoire lsblk - -# Create encrypted ZFS pool -sudo zpool create -o ashift=12 \ - -O encryption=on \ - -O keylocation=prompt \ - -O keyformat=passphrase \ - -O compression=lz4 \ - -O atime=off \ - -O recordsize=1M \ - -m /srv/vaultpg \ - vaultpg /dev/sdX - -# Create datasets -sudo zfs create -o recordsize=16K vaultpg/wiki-pg # PostgreSQL -sudo zfs create vaultpg/repos # Git repos -sudo zfs create vaultpg/pocket-grimoire # App data +# Note device names: /dev/sdX, /dev/sdY, /dev/sdZ ``` -**Create Media Pool (Rotated):** +**Create VAULT pool (encrypted):** ```bash -# For Personal Media SSD +# On Netgrimoire sudo zpool create -o ashift=12 \ -O encryption=on \ -O keylocation=prompt \ @@ -538,23 +536,184 @@ sudo zpool create -o ashift=12 \ -O compression=lz4 \ -O atime=off \ -O recordsize=1M \ - -m /srv/mediapg \ - mediapg /dev/sdY + -m /mnt/pocket-vault \ + pocket-vault /dev/sdX -sudo zfs create mediapg/library -sudo zfs create mediapg/library/movies -sudo zfs create mediapg/library/tv +# Enter STRONG passphrase when prompted +# Write down this passphrase - you'll need it on Pocket Grimoire -# For Family Media SSD (unencrypted) -# When swapping drives: -sudo zpool export mediapg -# Connect family SSD +# Create datasets matching Pocket structure +sudo zfs create -o recordsize=16K pocket-vault/wiki-pg +sudo zfs create pocket-vault/repos +sudo zfs create pocket-vault/pocket-grimoire +sudo zfs create pocket-vault/Green +sudo zfs create pocket-vault/Green/Pocket +sudo zfs create pocket-vault/Green/Pocket/stash +sudo zfs create pocket-vault/Green/Pocket/media +sudo zfs create pocket-vault/veracrypt-containers + +# Set ownership +sudo chown -R 1000:1000 /mnt/pocket-vault +``` + +**Create MEDIA-PERSONAL pool (encrypted):** +```bash +# On Netgrimoire +sudo zpool create -o ashift=12 \ + -O encryption=on \ + -O keylocation=prompt \ + -O keyformat=passphrase \ + -O compression=lz4 \ + -O atime=off \ + -O recordsize=1M \ + -m /mnt/pocket-media-personal \ + pocket-media-personal /dev/sdY + +# Enter STRONG passphrase (can be different from VAULT) +# Write down this passphrase + +# Create datasets +sudo zfs create pocket-media-personal/library +sudo zfs create pocket-media-personal/library/movies +sudo zfs create pocket-media-personal/library/tv + +# Set ownership +sudo chown -R 1000:1000 /mnt/pocket-media-personal +``` + +**Create MEDIA-FAMILY pool (unencrypted, optional now or later):** +```bash +# On Netgrimoire sudo zpool create -o ashift=12 \ -O compression=lz4 \ -O atime=off \ -O recordsize=1M \ - -m /srv/mediapg \ - mediapg /dev/sdY + -m /mnt/pocket-media-family \ + pocket-media-family /dev/sdZ + +# No encryption - family can use this drive on any system + +# Create datasets +sudo zfs create pocket-media-family/library +sudo zfs create pocket-media-family/library/movies +sudo zfs create pocket-media-family/library/tv + +# Set ownership +sudo chown -R 1000:1000 /mnt/pocket-media-family +``` + +**Perform initial sync to VAULT:** +```bash +# On Netgrimoire +# Sync data from Netgrimoire to Pocket VAULT drive + +# Sync Pocket directory structure +sudo rsync -avP --exclude='cache' \ + /export/vault/Green/Pocket/ \ + /mnt/pocket-vault/Green/Pocket/ + +# Or use ZFS send/receive for atomic snapshot +sudo zfs snapshot vault/Green/Pocket@initial +sudo zfs send vault/Green/Pocket@initial | \ + sudo zfs receive pocket-vault/Green/Pocket + +# Verify data +ls -lh /mnt/pocket-vault/Green/Pocket/ +du -sh /mnt/pocket-vault/Green/Pocket/ +``` + +**Populate MEDIA-PERSONAL (optional - curate content):** +```bash +# On Netgrimoire +# Copy curated personal media to Pocket media drive + +# Example: Copy favorite movies +sudo cp /export/vault/media/library/movies/favorites/*.mp4 \ + /mnt/pocket-media-personal/library/movies/ + +# Or use rsync for large transfers +sudo rsync -avP \ + /export/vault/media/library/tv/FavoriteShow/ \ + /mnt/pocket-media-personal/library/tv/FavoriteShow/ + +# Verify +du -sh /mnt/pocket-media-personal/library/ +``` + +**Export pools before disconnecting:** +```bash +# On Netgrimoire +# CRITICAL: Export pools before physically disconnecting drives + +sudo zpool export pocket-vault +sudo zpool export pocket-media-personal +sudo zpool export pocket-media-family # if created + +# Verify exported +zpool list +# Should NOT show pocket-* pools +``` + +**Physically disconnect drives from Netgrimoire.** + +### 5. Configure ZFS Pools on Pocket Grimoire + +**Now connect drives to Pocket Grimoire:** +- VAULT → Anker USB-A port #2 +- MEDIA-PERSONAL or MEDIA-FAMILY → Raspberry Pi USB 3.0 port + +**Import and rename pools:** +```bash +# On Pocket Grimoire (SSH into Pi) +ssh user@pocket-grimoire.local + +# Import VAULT pool with new name +sudo zpool import pocket-vault vaultpg + +# Import MEDIA pool with new name +sudo zpool import pocket-media-personal mediapg +# Or: +# sudo zpool import pocket-media-family mediapg + +# Verify pools imported +zpool list +# Should show: vaultpg, mediapg +``` + +**Set mount points for Pocket Grimoire:** +```bash +# Set proper mount points +sudo zfs set mountpoint=/srv/vaultpg vaultpg +sudo zfs set mountpoint=/srv/mediapg mediapg + +# Create mount points +sudo mkdir -p /srv/vaultpg +sudo mkdir -p /srv/mediapg + +# Unmount and remount with new paths +sudo zfs unmount -a +sudo zfs mount -a + +# Verify mounted +df -h | grep srv +# Should show: +# vaultpg mounted on /srv/vaultpg +# mediapg mounted on /srv/mediapg + +# Verify data +ls /srv/vaultpg/Green/Pocket/ +ls /srv/mediapg/library/ +``` + +**Configure for headless unlock:** +```bash +# Set pools to NOT auto-mount on boot +# This prevents boot hanging waiting for passphrase + +sudo zfs set canmount=noauto vaultpg +sudo zfs set canmount=noauto mediapg + +# Pools will need manual unlock via SSH after boot ``` **Cap ZFS ARC Memory:** @@ -565,10 +724,248 @@ sudo nano /etc/modprobe.d/zfs.conf # Add this line (for 8GB Pi, cap at 1GB): options zfs zfs_arc_max=1073741824 -# Reboot to apply +# Save and apply +sudo update-initramfs -u sudo reboot ``` +### 6. Create Headless Unlock Script + +**After reboot, SSH back in and create unlock script:** + +```bash +sudo nano /usr/local/sbin/unlock-pocket-grimoire.sh +``` + +```bash +#!/bin/bash +# Unlock Pocket Grimoire encrypted ZFS pools (headless operation) + +set -e + +echo "==========================================" +echo " Pocket Grimoire ZFS Unlock (Headless)" +echo "==========================================" +echo + +# Check if pools are already unlocked +if zfs list vaultpg &>/dev/null && mount | grep -q /srv/vaultpg; then + echo "✓ vaultpg already unlocked and mounted" +else + # Import pool if needed + if ! zpool list vaultpg &>/dev/null; then + echo "Importing vaultpg pool..." + sudo zpool import vaultpg + fi + + # Unlock Vault pool + echo "Unlocking vaultpg (VAULT SSD)..." + sudo zfs load-key vaultpg + + # Mount all vaultpg datasets + sudo zfs mount vaultpg + sudo zfs mount -a + + if mount | grep -q /srv/vaultpg; then + echo "✓ vaultpg unlocked and mounted at /srv/vaultpg" + else + echo "✗ Failed to mount vaultpg" + exit 1 + fi +fi + +echo + +# Check if media pool is present and unlock +if zpool list mediapg &>/dev/null; then + if zfs list mediapg &>/dev/null && mount | grep -q /srv/mediapg; then + echo "✓ mediapg already unlocked and mounted" + else + echo "Unlocking mediapg (MEDIA-PERSONAL or MEDIA-FAMILY)..." + + # Check if encrypted (MEDIA-PERSONAL) or not (MEDIA-FAMILY) + if zfs get encryption mediapg | grep -q "encryption.*on"; then + sudo zfs load-key mediapg + fi + + sudo zfs mount mediapg + sudo zfs mount -a + + if mount | grep -q /srv/mediapg; then + echo "✓ mediapg unlocked and mounted at /srv/mediapg" + else + echo "✗ Failed to mount mediapg" + exit 1 + fi + fi +else + echo "ℹ mediapg pool not found (media drive may not be connected)" +fi + +echo + +# Optional: Mount VeraCrypt containers +if [ -f /usr/local/sbin/mount-veracrypt-vault.sh ]; then + echo "VeraCrypt container found. Mount now? (y/n)" + read -r response + if [[ "$response" == "y" ]]; then + /usr/local/sbin/mount-veracrypt-vault.sh + fi +fi + +echo +echo "==========================================" +echo " Starting Docker Services" +echo "==========================================" +echo + +# Start Docker service +if ! systemctl is-active --quiet docker; then + echo "Starting Docker..." + sudo systemctl start docker + sleep 3 +fi + +# Start containers +echo "Starting Wiki.js stack..." +cd /srv/pocket-grimoire/stacks/wikijs && docker compose up -d + +echo "Starting Jellyfin stack..." +cd /srv/pocket-grimoire/stacks/jellyfin && docker compose up -d + +echo "Starting Stash stack..." +if [ -d /srv/pocket-grimoire/stacks/stash ]; then + cd /srv/pocket-grimoire/stacks/stash && docker compose up -d +fi + +# Optional containers +if [ -d /srv/pocket-grimoire/stacks/filebrowser ]; then + echo "Starting File Browser..." + cd /srv/pocket-grimoire/stacks/filebrowser && docker compose up -d +fi + +echo +echo "==========================================" +echo " Pocket Grimoire Ready!" +echo "==========================================" +echo +echo "Services available at:" +echo " Wiki.js: http://pocket-grimoire.local:3000" +echo " Jellyfin: http://pocket-grimoire.local:8096" +echo " Stash: http://pocket-grimoire.local:9999" +echo " File Browser: http://pocket-grimoire.local:8080" +echo +echo "Total unlock time: $(($SECONDS / 60)) minutes $(($SECONDS % 60)) seconds" +echo +``` + +```bash +sudo chmod +x /usr/local/sbin/unlock-pocket-grimoire.sh +``` + +### 7. Disable Docker Auto-Start (Headless Configuration) + +**Prevent Docker from starting before ZFS pools are unlocked:** + +```bash +# Disable Docker auto-start on boot +sudo systemctl disable docker + +# Docker will be started manually by unlock script +``` + +**Or, configure Docker to wait for ZFS (if you prefer):** + +```bash +sudo mkdir -p /etc/systemd/system/docker.service.d +sudo nano /etc/systemd/system/docker.service.d/wait-for-zfs.conf +``` + +```ini +[Unit] +# Don't start Docker until after manual ZFS unlock +After=zfs-mount.service +Wants=zfs-mount.service + +[Service] +# Restart Docker if it fails (ZFS not ready) +Restart=on-failure +RestartSec=10 +``` + +```bash +sudo systemctl daemon-reload +``` + +**Recommended: Just disable auto-start and use unlock script.** + +### 8. Test Headless Unlock Procedure + +**Test at home before traveling:** + +```bash +# 1. Reboot Pi +sudo reboot + +# 2. Wait 2-3 minutes for boot (don't connect monitor/keyboard) + +# 3. SSH from laptop +ssh user@pocket-grimoire.local + +# 4. Run unlock script +/usr/local/sbin/unlock-pocket-grimoire.sh + +# Enter passphrases when prompted: +# - VAULT passphrase +# - MEDIA-PERSONAL passphrase (if encrypted) +# - VeraCrypt password (if using) + +# 5. Wait for Docker containers to start + +# 6. Verify services running +docker ps + +# 7. Access from browser +# http://pocket-grimoire.local:3000 +# http://pocket-grimoire.local:8096 +# http://pocket-grimoire.local:9999 + +# 8. Verify data accessible +ls /srv/vaultpg/Green/Pocket/ +ls /srv/mediapg/library/ +``` + +**If everything works, you're ready for travel!** + +### 9. Quick Manual Unlock (If Script Fails) + +```bash +# SSH into Pocket Grimoire +ssh user@pocket-grimoire.local + +# Import pools if needed +sudo zpool import vaultpg +sudo zpool import mediapg + +# Load encryption keys +sudo zfs load-key vaultpg +sudo zfs load-key mediapg # Only if MEDIA-PERSONAL (encrypted) + +# Mount all datasets +sudo zfs mount -a + +# Verify mounted +df -h | grep srv + +# Start Docker +sudo systemctl start docker + +# Start containers manually +cd /srv/pocket-grimoire/stacks/wikijs && docker compose up -d +cd /srv/pocket-grimoire/stacks/jellyfin && docker compose up -d +cd /srv/pocket-grimoire/stacks/stash && docker compose up -d +``` + **Configure ZFS to Wait for Passphrase on Boot:** ```bash # Edit /etc/systemd/system/zfs-load-key.service @@ -1319,24 +1716,70 @@ sudo umount /mnt/pocket-media ### 8. Document Passphrases - [ ] ZFS encryption passphrases (written down, secured) + - VAULT (vaultpg): [write passphrase on paper] + - MEDIA-PERSONAL (mediapg): [write passphrase on paper] + - MEDIA-FAMILY: N/A (unencrypted) - [ ] VeraCrypt container passwords (if using, written down, secured) - [ ] WiFi credentials for travel router (portapotty network) - [ ] Jellyfin admin password - [ ] Wiki.js admin password +- [ ] Stash admin password - [ ] Keep all passphrases in secure location separate from device -### 9. Test VeraCrypt Containers (If Using) +### 9. Test Headless Unlock Procedure (CRITICAL) ```bash -# Verify container can mount -sudo veracrypt --text --mount \ - /srv/vaultpg/veracrypt-containers/vault.vc \ - /mnt/veracrypt/vault1 +# At home, test the exact hotel deployment workflow -# Access files -ls /mnt/veracrypt/vault1 +# 1. Reboot Pi without monitor/keyboard attached +sudo reboot -# Unmount -sudo veracrypt --text --dismount /mnt/veracrypt/vault1 +# 2. Wait 2-3 minutes for boot + +# 3. SSH from laptop +ssh user@pocket-grimoire.local + +# 4. Run unlock script +/usr/local/sbin/unlock-pocket-grimoire.sh + +# 5. Enter passphrases when prompted +# - VAULT passphrase +# - MEDIA-PERSONAL passphrase (if encrypted) +# - VeraCrypt password (if applicable) + +# 6. Wait for Docker containers to start (~30 seconds) + +# 7. Verify all services running +docker ps + +# 8. Test access from browser +# http://pocket-grimoire.local:3000 (Wiki.js) +# http://pocket-grimoire.local:8096 (Jellyfin) +# http://pocket-grimoire.local:9999 (Stash) + +# 9. Test media playback in Jellyfin +# 10. Test Stash preview playback +# 11. Test NFS mount from laptop (optional) +``` + +**If anything fails during this test, debug at home before traveling!** + +### 10. Verify Data Synced from Netgrimoire +```bash +# Check Vault data present +ls /srv/vaultpg/Green/Pocket/stash/ +ls /srv/vaultpg/Green/Pocket/media/ +du -sh /srv/vaultpg/Green/Pocket/ + +# Check media drive populated (if applicable) +ls /srv/mediapg/library/ +du -sh /srv/mediapg/ + +# Verify Stash database and previews +ls -lh /srv/vaultpg/Green/Pocket/stash/config/ +# Should show: stash-go.sqlite + +ls /srv/vaultpg/Green/Pocket/stash/generated/ | wc -l +# Should show: hundreds of preview files ``` --- @@ -1347,52 +1790,109 @@ sudo veracrypt --text --dismount /mnt/veracrypt/vault1 ### Physical Setup (5 minutes) 1. Unpack Pocket Grimoire enclosure -2. Connect Beryl AX to hotel WiFi (configure via phone app) -3. Connect Pi to Beryl AX via Ethernet +2. Connect Beryl AX to hotel WiFi (configure via phone app or admin panel) +3. Connect Pi to Beryl AX via Ethernet (CAT5 cable) 4. Plug Anker Prime into wall outlet -5. Connect all USB devices to Anker Prime +5. Connect all USB devices to Anker Prime: + - VAULT SSD → Anker USB-A port #2 + - Media SSD (PERSONAL or FAMILY) → Pi USB 3.0 port + - Beryl AX → Anker USB-C retractable port + - Pi → Anker USB-A port #1 6. Power on (wait 2-3 minutes for boot) -### ZFS Unlock (2 minutes) +### SSH Connection (1 minute) +```bash +# From laptop (connected to portapotty WiFi) +ssh user@pocket-grimoire.local + +# If .local doesn't work, find Pi's IP: +# - Check Beryl AX admin: http://192.168.8.1 +# - Look for "pocket-grimoire" in client list +# - SSH via IP: ssh user@192.168.8.50 +``` + +### ZFS Unlock (2-3 minutes) +```bash +# Run unlock script +/usr/local/sbin/unlock-pocket-grimoire.sh + +# Script will prompt for passphrases: +# Enter passphrase for 'vaultpg': [type VAULT passphrase] +# Enter passphrase for 'mediapg': [type MEDIA-PERSONAL passphrase] +# (MEDIA-FAMILY is unencrypted, no passphrase needed) + +# Script automatically: +# - Unlocks ZFS pools +# - Mounts all datasets +# - Starts Docker service +# - Starts all containers (Wiki.js, Jellyfin, Stash) +# - Displays service URLs + +# Total unlock time: ~2-3 minutes +``` + +### Verify Services (1 minute) +### Verify Services (1 minute) +```bash +# Check Docker containers running +docker ps + +# Should show: +# pocketgrimoire_wikijs +# pocketgrimoire_db +# pocketgrimoire_jellyfin +# pocketgrimoire_stash + +# Check ZFS pools mounted +df -h | grep srv + +# Should show: +# vaultpg mounted on /srv/vaultpg +# mediapg mounted on /srv/mediapg +``` + +### Access Services (1 minute) +**From laptop browser (connected to portapotty WiFi):** +- Wiki.js: `http://pocket-grimoire.local:3000` +- Jellyfin: `http://pocket-grimoire.local:8096` +- Stash: `http://pocket-grimoire.local:9999` +- File Browser: `http://pocket-grimoire.local:8080` (if enabled) + +**From Onn Streaming Boxes:** +- Configure Jellyfin app: Server `http://pocket-grimoire.local:8096` +- Configure StashApp: Server `http://pocket-grimoire.local:9999` + +**Total setup time: ~10-12 minutes** + +### If Unlock Script Fails + +**Manual unlock procedure:** ```bash # SSH into Pi ssh user@pocket-grimoire.local -# If ZFS pools didn't auto-unlock, unlock manually +# Import pools +sudo zpool import vaultpg +sudo zpool import mediapg + +# Load encryption keys sudo zfs load-key vaultpg +sudo zfs load-key mediapg # Only if MEDIA-PERSONAL (encrypted) + +# Mount datasets sudo zfs mount -a -sudo zfs load-key mediapg -sudo zfs mount -a +# Verify +df -h | grep srv -# Verify pools mounted -zfs list +# Start Docker +sudo systemctl start docker + +# Start containers +cd /srv/pocket-grimoire/stacks/wikijs && docker compose up -d +cd /srv/pocket-grimoire/stacks/jellyfin && docker compose up -d +cd /srv/pocket-grimoire/stacks/stash && docker compose up -d ``` - -### VeraCrypt Mount (Optional, 1 minute) -```bash -# If using VeraCrypt containers -# SSH into Pi (if not already) -ssh user@pocket-grimoire.local - -# Mount VeraCrypt container(s) -sudo /usr/local/sbin/mount-veracrypt-vault.sh -# Enter password when prompted - -# Verify mounted -df -h /mnt/veracrypt/vault1 -ls /mnt/veracrypt/vault1 - -# Or mount manually: -sudo veracrypt --text --mount \ - /srv/vaultpg/veracrypt-containers/vault.vc \ - /mnt/veracrypt/vault1 - -# List all mounted VeraCrypt volumes -veracrypt --text --list -``` - -### Verify Services (2 minutes) ```bash # Check Docker containers docker ps @@ -1646,11 +2146,11 @@ htop ## Shutdown Procedure -**Proper shutdown to protect encrypted ZFS pools:** +**Proper shutdown to protect encrypted ZFS pools (headless operation):** -### From SSH +### From SSH (Recommended) ```bash -# SSH into Pi +# SSH into Pi from laptop ssh user@pocket-grimoire.local # Stop Docker containers @@ -1660,6 +2160,9 @@ docker compose down cd /srv/pocket-grimoire/stacks/jellyfin docker compose down +cd /srv/pocket-grimoire/stacks/stash +docker compose down + # Optional: Stop other containers cd /srv/pocket-grimoire/stacks/filebrowser docker compose down @@ -1678,22 +2181,57 @@ sudo zfs unmount -a sudo zpool export vaultpg sudo zpool export mediapg +# Verify pools exported +zpool list +# Should NOT show vaultpg or mediapg + # Shutdown Pi sudo shutdown -h now # Wait 30 seconds for complete shutdown -# Red LED will turn off when safe to unplug +# Pi's green ACT LED will stop blinking +# Red power LED will turn off +# Safe to unplug power ``` +**Total shutdown time: ~2-3 minutes** + ### Emergency Shutdown -**If SSH is unavailable:** +**If SSH is unavailable or Pi is unresponsive:** -1. Unplug Ethernet cable from Pi (stops network activity) -2. Wait 10 seconds -3. Unplug power from Anker Prime -4. ZFS pools and VeraCrypt containers may need recovery on next boot (usually auto-repairs) +1. Stop all network activity: + - Unplug Ethernet cable from Pi + - Wait 10 seconds -**Note:** ZFS is resilient, but proper shutdown is always better. VeraCrypt containers are generally safe with sudden unmount. +2. Power off: + - Unplug power from Anker Prime (pulls power from everything) + - Wait 10 seconds + +3. Consequences: + - ZFS pools may need recovery on next boot (usually auto-repairs) + - VeraCrypt containers are generally safe with sudden unmount + - Docker containers will need restart + - No data loss expected (ZFS is resilient) + +**Note:** ZFS and VeraCrypt are resilient to sudden power loss, but proper shutdown is always better for data integrity. + +### Shutdown Checklist + +Before leaving hotel: +- [ ] SSH into Pocket Grimoire +- [ ] Stop all Docker containers +- [ ] Unmount VeraCrypt (if using) +- [ ] Export ZFS pools (vaultpg, mediapg) +- [ ] Shutdown Pi (`sudo shutdown -h now`) +- [ ] Wait for Pi LEDs to turn off (30 seconds) +- [ ] Unplug power from Anker Prime +- [ ] Disconnect and pack all equipment + +**Never skip the ZFS export step!** Exporting pools ensures: +- All data is flushed to disk +- Filesystem is marked clean +- Prevents corruption +- Allows pools to be imported cleanly on next boot ---