923 lines
39 KiB
HTML
923 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>KingDezigns â NAS08 Automated Backup Setup Summary</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500&display=swap" rel="stylesheet">
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #f7f6f2;
|
||
--surface: #ffffff;
|
||
--surface-alt: #f1f0eb;
|
||
--border: #e2e0d8;
|
||
--border-strong: #c8c5ba;
|
||
--text-primary: #1a1917;
|
||
--text-secondary: #6b6960;
|
||
--text-muted: #9b9890;
|
||
--green-bg: #eaf3de;
|
||
--green-border: #97c459;
|
||
--green-text: #2d5a0e;
|
||
--red-bg: #fcebeb;
|
||
--red-border: #f09595;
|
||
--red-text: #7a1f1f;
|
||
--amber-bg: #faeeda;
|
||
--amber-border: #fac775;
|
||
--amber-text: #633806;
|
||
--coral-bg: #faece7;
|
||
--coral-border: #f0997b;
|
||
--coral-text: #4a1b0c;
|
||
--mono: 'IBM Plex Mono', monospace;
|
||
--sans: 'IBM Plex Sans', sans-serif;
|
||
}
|
||
|
||
body {
|
||
font-family: var(--sans);
|
||
font-size: 14px;
|
||
background: var(--bg);
|
||
color: var(--text-primary);
|
||
line-height: 1.7;
|
||
padding: 2.5rem 1.5rem;
|
||
}
|
||
|
||
.page { max-width: 860px; margin: 0 auto; }
|
||
|
||
header {
|
||
border-bottom: 1px solid var(--border-strong);
|
||
padding-bottom: 1.25rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
header h1 {
|
||
font-family: var(--mono);
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.02em;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.meta {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
font-family: var(--mono);
|
||
display: flex;
|
||
gap: 16px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.section { margin-bottom: 2.25rem; }
|
||
|
||
.section-title {
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-muted);
|
||
margin-bottom: 1rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.section-title::before {
|
||
content: attr(data-num);
|
||
background: var(--text-primary);
|
||
color: var(--bg);
|
||
font-size: 10px;
|
||
width: 18px;
|
||
height: 18px;
|
||
border-radius: 50%;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.section-title::after {
|
||
content: '';
|
||
flex: 1;
|
||
height: 1px;
|
||
background: var(--border);
|
||
}
|
||
|
||
p { margin-bottom: 0.75rem; color: var(--text-primary); }
|
||
p:last-child { margin-bottom: 0; }
|
||
|
||
.badge-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 1rem; }
|
||
|
||
.badge {
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
padding: 3px 10px;
|
||
border-radius: 3px;
|
||
border: 1px solid var(--border);
|
||
background: var(--surface);
|
||
color: var(--text-secondary);
|
||
}
|
||
.badge.red { background: var(--red-bg); color: var(--red-text); border-color: var(--red-border); }
|
||
.badge.green { background: var(--green-bg); color: var(--green-text); border-color: var(--green-border); }
|
||
.badge.amber { background: var(--amber-bg); color: var(--amber-text); border-color: var(--amber-border); }
|
||
|
||
.step {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
padding: 1rem 1.25rem;
|
||
margin-bottom: 0.6rem;
|
||
}
|
||
|
||
.step-header {
|
||
font-family: var(--mono);
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 0.6rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.step-num {
|
||
background: var(--surface-alt);
|
||
border: 1px solid var(--border);
|
||
border-radius: 50%;
|
||
width: 20px;
|
||
height: 20px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 10px;
|
||
flex-shrink: 0;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
pre {
|
||
background: var(--surface-alt);
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
padding: 10px 12px;
|
||
font-family: var(--mono);
|
||
font-size: 12px;
|
||
overflow-x: auto;
|
||
margin: 0.5rem 0;
|
||
white-space: pre;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
code {
|
||
font-family: var(--mono);
|
||
font-size: 12px;
|
||
background: var(--surface-alt);
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
border: 1px solid var(--border);
|
||
}
|
||
|
||
.why {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
margin-top: 0.4rem;
|
||
padding-left: 2px;
|
||
}
|
||
.why::before { content: 'â³ why: '; font-family: var(--mono); }
|
||
|
||
.note {
|
||
font-size: 12px;
|
||
color: var(--text-secondary);
|
||
margin-top: 0.5rem;
|
||
padding-left: 2px;
|
||
}
|
||
.note::before { content: 'ğ '; }
|
||
|
||
.result { font-size: 12px; margin-top: 0.35rem; padding-left: 2px; }
|
||
.result.pass { color: var(--green-text); }
|
||
.result.pass::before { content: 'â '; font-family: var(--mono); }
|
||
.result.fail { color: var(--red-text); }
|
||
.result.fail::before { content: 'â '; font-family: var(--mono); }
|
||
.result.neutral { color: var(--text-secondary); }
|
||
.result.neutral::before { content: 'â '; font-family: var(--mono); }
|
||
|
||
.callout { border-radius: 6px; padding: 1rem 1.25rem; margin-bottom: 0.75rem; }
|
||
.callout.coral { background: var(--coral-bg); border: 1px solid var(--coral-border); }
|
||
.callout.coral p { color: var(--coral-text); }
|
||
.callout.coral pre { background: #fdf0eb; border-color: var(--coral-border); }
|
||
.callout.amber { background: var(--amber-bg); border: 1px solid var(--amber-border); }
|
||
.callout.amber p { color: var(--amber-text); }
|
||
.callout.amber pre { background: #fdf5e6; border-color: var(--amber-border); }
|
||
.callout.amber code { background: #fdf5e6; border-color: var(--amber-border); color: var(--amber-text); }
|
||
.callout.green { background: var(--green-bg); border: 1px solid var(--green-border); }
|
||
.callout.green p { color: var(--green-text); }
|
||
.callout.green pre { background: #d8efc0; border-color: var(--green-border); }
|
||
|
||
.clue-label {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.07em;
|
||
color: var(--amber-text);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.phase-label {
|
||
font-family: var(--mono);
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-primary);
|
||
background: var(--surface-alt);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: 4px;
|
||
padding: 5px 12px;
|
||
margin: 1.5rem 0 0.6rem 0;
|
||
display: inline-block;
|
||
}
|
||
|
||
.script-box { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; margin-bottom: 1rem; }
|
||
.script-row { padding: 0.75rem 1.25rem; border-bottom: 1px solid var(--border); }
|
||
.script-row:last-child { border-bottom: none; }
|
||
.script-label { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-muted); margin-bottom: 6px; }
|
||
.script-row pre { margin: 0; }
|
||
|
||
table { width: 100%; border-collapse: collapse; font-size: 13px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
|
||
th { text-align: left; padding: 8px 14px; font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); background: var(--surface-alt); border-bottom: 1px solid var(--border); font-weight: 500; }
|
||
td { padding: 9px 14px; border-bottom: 1px solid var(--border); vertical-align: top; line-height: 1.5; }
|
||
tr:last-child td { border-bottom: none; }
|
||
|
||
.footer {
|
||
font-family: var(--mono);
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
margin-top: 2.5rem;
|
||
padding-top: 1rem;
|
||
border-top: 1px solid var(--border);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="page">
|
||
|
||
<header>
|
||
<h1>KingDezigns â NAS08 Automated Backup Setup summary</h1>
|
||
<div class="meta">
|
||
<span>ğ
2026-05-11</span>
|
||
<span>ğ¥ NAS08 / Raspberry Pi 5</span>
|
||
<span>âï¸ OpenMediaVault (OMV)</span>
|
||
<span>ğ³ Docker Compose</span>
|
||
<span>ğ¾ ZFS RAIDZ2 â Penta SATA HAT</span>
|
||
</div>
|
||
</header>
|
||
|
||
|
||
<!-- SECTION 1 â THE GOAL -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="1">The goal</div>
|
||
|
||
<p>NAS08 had no automated recovery path. A software or hardware failure â corrupt SD card, bad OMV update, runaway script â would have required a full manual rebuild of OMV, all Docker containers, Pi-hole, Plex, Vaultwarden, Nextcloud, and nginx-proxy-manager with no documented procedure and no saved state.</p>
|
||
|
||
<div class="badge-row">
|
||
<span class="badge amber">No OS backup</span>
|
||
<span class="badge amber">No automated recovery plan</span>
|
||
<span class="badge amber">No ZFS restore procedure</span>
|
||
<span class="badge amber">Plex music library previously lost to a bad script</span>
|
||
</div>
|
||
|
||
<p>The goal was to create a fully automated, scheduled backup of the OS and configuration layer â not the NAS data drives â that could restore NAS08 to full operation within approximately 90 minutes after a failure, with no manual intervention during normal operation.</p>
|
||
|
||
<div class="callout amber" style="margin-top:1rem;">
|
||
<div class="clue-label">â Critical â store this document off NAS08</div>
|
||
<p>Keep a copy of this document on your workstation, a USB drive, or in print. If NAS08 is down you cannot read files stored on it.</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 2 â DIAGNOSTIC COMMANDS -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="2">Discovery commands run & why</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">1</span> Locate Docker app data directories</div>
|
||
<pre>ls /export/kingdezigns-all/Docker
|
||
ls /export/kingdezigns-all/Docker/Compose</pre>
|
||
<p class="why">Needed to confirm where application configs and compose files lived before writing backup paths into the script.</p>
|
||
<p class="result pass">Docker root confirmed at <code>/export/kingdezigns-all/Docker/</code> with Compose subfolder containing all stacks: nextcloud, nginx-proxy-manager, pihole, plex, vaultwarden.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">2</span> Check first archive size and contents</div>
|
||
<pre>ls -lh /export/kingdezigns-all/Backups/NAS08/nas08-backup-2026-05-11.tar.gz
|
||
tar -tzf /export/kingdezigns-all/Backups/NAS08/nas08-backup-2026-05-11.tar.gz | head -40</pre>
|
||
<p class="why">Verified the archive was created and inspected its contents to catch bloat or missing sections.</p>
|
||
<p class="result fail">Archive was 1.4GB â Vaultwarden icon_cache and full Plex config were included without exclusions, inflating the size unnecessarily.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">3</span> Measure sizes across all Docker folders</div>
|
||
<pre>sudo du -sh /export/kingdezigns-all/Docker/Compose/*/
|
||
sudo du -sh /export/kingdezigns-all/Docker/pihole/
|
||
sudo du -sh /export/kingdezigns-all/Docker/plex/</pre>
|
||
<p class="why">Identify where archive bulk was coming from before adding exclusions.</p>
|
||
<p class="result neutral">Plex at 1.8GB was the dominant size. Pi-hole at 27MB acceptable as-is. Compose folders were tiny (3.5Kâ7.4M each).</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">4</span> Drill into Plex config subfolder breakdown</div>
|
||
<pre>sudo du -sh /export/kingdezigns-all/Docker/plex/config/Library/Application\ Support/Plex\ Media\ Server/*/</pre>
|
||
<p class="why">Identify which Plex subfolders were safe to exclude vs which contained manually curated data worth preserving.</p>
|
||
<p class="result pass">Cache (24M), Logs (2M), Crash Reports (23K) identified as safe exclusions. Media (655M) and Metadata (956M) confirmed as music organization and library data â kept.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">5</span> Inspect ZFS pool configuration</div>
|
||
<pre>sudo zpool list
|
||
sudo zpool status
|
||
sudo zfs list</pre>
|
||
<p class="why">Document exact pool name, drive identifiers, RAID type, and mount point needed for recovery plan ZFS steps.</p>
|
||
<p class="result pass">Pool: <code>kingdezignsnas</code> â RAIDZ2 across 4 Ã 1TB SSDs â ONLINE â mounted at <code>/kingdezignsnas</code> â last scrub clean, no errors.</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 3 â ISSUES ENCOUNTERED -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="3">Issues encountered during setup</div>
|
||
|
||
<div class="callout coral">
|
||
<p>On first run the backup destination directory did not exist, causing the logging system to fail immediately before any backup work could begin.</p>
|
||
</div>
|
||
|
||
<div class="callout amber">
|
||
<div class="clue-label">â Error â first run</div>
|
||
<p style="margin-bottom:6px;">Script failed with a tee write error before reaching any backup steps.</p>
|
||
<pre>tee: /export/kingdezigns-all/Backups/NAS08/backup.log: No such file or directory</pre>
|
||
<p style="margin-top:8px;"><code>Backups/NAS08/</code> had not been created yet. Fixed by creating it manually. The script now handles this automatically via <code>mkdir -p</code> on every run.</p>
|
||
</div>
|
||
|
||
<div class="callout coral" style="margin-top: 0.75rem;">
|
||
<p>SCP to <code>/usr/local/bin/</code> failed with permission denied â that path requires root SCP access which rufusking does not have.</p>
|
||
</div>
|
||
|
||
<div class="callout green">
|
||
<p><strong>Resolution:</strong> Created <code>/usr/scripts/omv/</code> owned by rufusking. SCP now works without root. Script still runs with <code>sudo</code> for root-level operations at runtime.</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 4 â SCRIPTS / FILES CREATED -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="4">Scripts / files created</div>
|
||
|
||
<div class="script-box">
|
||
<div class="script-row">
|
||
<div class="script-label">Backup Script Location</div>
|
||
<pre>/usr/scripts/omv/nas08-backup.sh</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">What It Backs Up</div>
|
||
<pre>OMV config â omv-confbak export + raw /etc/openmediavault/config.xml
|
||
/etc â full system config (network, fstab, nginx, cron, etc.)
|
||
Docker Compose â all stacks + global.env (vaultwarden icon_cache excluded)
|
||
Pi-hole data â /export/kingdezigns-all/Docker/pihole/
|
||
Plex data â full config minus Cache/, Logs/, Crash Reports/
|
||
Media/ and Metadata/ KEPT (music org + library data)
|
||
Shared folder â /export/kingdezigns-all/Docker/Shared/
|
||
Crontab â root crontab + /etc/cron.d entries
|
||
MANIFEST.txt â baked-in restore instructions and OMV version info</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">Output Paths</div>
|
||
<pre>/export/kingdezigns-all/Backups/NAS08/nas08-backup-YYYY-MM-DD.tar.gz
|
||
/export/kingdezigns-all/Backups/NAS08/backup.log</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">Retention</div>
|
||
<pre>10 archives retained (~30 days coverage) â older archives auto-deleted on each run</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">Expected Size</div>
|
||
<pre>~1.4GB per archive â ~14GB total at max retention
|
||
450GB free on data drive â well within capacity</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">Schedule via OMV UI</div>
|
||
<pre>System â Scheduled Jobs â Add
|
||
Name: NAS08 Backup
|
||
Command: /usr/scripts/omv/nas08-backup.sh
|
||
Minute: 0
|
||
Hour: 2
|
||
Day of month: */3
|
||
Month: *
|
||
Day of week: *
|
||
Runs automatically at 2:00 AM every 3 days</pre>
|
||
</div>
|
||
<div class="script-row">
|
||
<div class="script-label">Manual Run</div>
|
||
<pre>sudo /usr/scripts/omv/nas08-backup.sh
|
||
# or: OMV UI â System â Scheduled Jobs â Run</pre>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 5 â FULL RECOVERY PROCEDURE -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="5">Full recovery procedure</div>
|
||
|
||
<div class="callout amber">
|
||
<div class="clue-label">â Before You Start</div>
|
||
<p>You need: a workstation with internet, a replacement SD card (16GB+), Raspberry Pi Imager, SSH access, and your rufusking password. Physically connect all 4 ZFS drives to the Penta SATA HAT before powering on.</p>
|
||
</div>
|
||
|
||
<!-- PHASE 1 -->
|
||
<div class="phase-label">Phase 1 â Flash the OS</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">1</span> Download Raspberry Pi Imager â workstation</div>
|
||
<pre>https://www.raspberrypi.com/software/</pre>
|
||
<p class="note">Download and install for your OS (Windows / Mac / Linux).</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">2</span> Flash Raspberry Pi OS Lite (64-bit) â workstation</div>
|
||
<pre>Open Raspberry Pi Imager
|
||
â Choose Device : Raspberry Pi 5
|
||
â Choose OS : Raspberry Pi OS Lite (64-bit) â NOT the Desktop version
|
||
â Choose Storage : your SD card
|
||
â Next â Edit Settings:
|
||
Hostname : NAS08
|
||
Username : rufusking
|
||
Password : your usual password
|
||
Services : Enable SSH â Use password authentication
|
||
â Save â Yes â Yes (confirm flash and verify)</pre>
|
||
<p class="note">Wait for the flash and verify to complete fully before removing the SD card.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">3</span> Boot the Raspberry Pi</div>
|
||
<pre>1. Insert SD card into Raspberry Pi 5
|
||
2. Connect Penta SATA HAT with all 4 ZFS drives plugged in
|
||
3. Power on
|
||
4. Wait 2â3 minutes for first boot to complete</pre>
|
||
</div>
|
||
|
||
<!-- PHASE 2 -->
|
||
<div class="phase-label">Phase 2 â Install OpenMediaVault</div>
|
||
|
||
<div class="callout amber">
|
||
<div class="clue-label">â Note</div>
|
||
<p>The OMV install method may change in the future. Verify the current script at <code>https://github.com/OpenMediaVault-Plugin-Developers/installScript</code> before running. Steps below reflect the method as of 2026-05-11.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">4</span> SSH into NAS08 â workstation</div>
|
||
<pre>ssh rufusking@192.168.150.35</pre>
|
||
<p class="note">If the static IP is not yet assigned, log into UniFi at <code>http://192.168.100.1</code>, find NAS08 in Clients, and SSH to its temporary IP instead.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">5</span> Update the OS â NAS08</div>
|
||
<pre>sudo apt-get update && sudo apt-get upgrade -y</pre>
|
||
<p class="note">Wait for the prompt to return before continuing. This may take a few minutes.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">6</span> Install OpenMediaVault â NAS08</div>
|
||
<pre>sudo wget -O - https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install | sudo bash</pre>
|
||
<p class="note">Takes 10â20 minutes. The Pi may reboot automatically. If it does, SSH back in and wait for the script to finish. Do not interrupt it.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">7</span> Verify OMV is running â browser on workstation</div>
|
||
<pre>http://192.168.150.35
|
||
|
||
Default login:
|
||
Username: admin
|
||
Password: openmediavault</pre>
|
||
<p class="note">Confirm the OMV login page loads. Do not configure anything yet â the restore handles all configuration.</p>
|
||
</div>
|
||
|
||
<!-- PHASE 3 -->
|
||
<div class="phase-label">Phase 3 â Enable PCIe for Penta SATA HAT</div>
|
||
|
||
<div class="callout coral">
|
||
<p>The Raspberry Pi 5 does not expose PCIe by default. Without enabling it, the Penta SATA HAT and all 4 ZFS drives are completely invisible to the OS. This must be done before any ZFS commands will work.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">8</span> Edit boot config to enable PCIe â NAS08</div>
|
||
<pre>sudo nano /boot/firmware/config.txt</pre>
|
||
<p>Scroll to find the line <code>arm_boost=1</code> and add these lines immediately after it:</p>
|
||
<pre># Enable PCIe
|
||
dtparam=pciex1
|
||
dtparam=pciex1_gen=3</pre>
|
||
<p>Save and exit:</p>
|
||
<pre>Ctrl+X â Y â Enter</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">9</span> Reboot to apply PCIe config â NAS08</div>
|
||
<pre>sudo reboot</pre>
|
||
<p>Wait 2â3 minutes, then SSH back in:</p>
|
||
<pre>ssh rufusking@192.168.150.35</pre>
|
||
</div>
|
||
|
||
<!-- PHASE 4 -->
|
||
<div class="phase-label">Phase 4 â Import ZFS Pool</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">10</span> Install ZFS tools â NAS08</div>
|
||
<pre>sudo apt-get install -y zfsutils-linux</pre>
|
||
<p class="note">Installs the ZFS kernel modules and CLI tools. Required on every fresh OS install.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">11</span> Verify all 4 drives are visible â NAS08</div>
|
||
<pre>lsblk</pre>
|
||
<p>You should see 4 drives listed (typically <code>sda</code> through <code>sdd</code>). If any are missing, check physical connections and reboot:</p>
|
||
<pre>sudo reboot
|
||
# SSH back in, then run lsblk again</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">12</span> Import the ZFS pool â NAS08</div>
|
||
<pre>sudo zpool import kingdezignsnas</pre>
|
||
<p class="note">The pool name <code>kingdezignsnas</code> is stored on the drives themselves â it will be recognized on a fresh OS automatically. If the previous OS failed uncleanly, force the import:</p>
|
||
<pre># Only use -f if the standard import fails:
|
||
sudo zpool import -f kingdezignsnas</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">13</span> Verify pool imported correctly â NAS08</div>
|
||
<pre>sudo zpool status</pre>
|
||
<p>All 4 drives must show <strong>ONLINE</strong>. Expected output:</p>
|
||
<pre> pool: kingdezignsnas
|
||
state: ONLINE
|
||
config:
|
||
kingdezignsnas ONLINE
|
||
raidz2-0 ONLINE
|
||
ata-CT1000BX500SSD1_2539E9CB8529 ONLINE
|
||
ata-CT1000BX500SSD1_2534E9C97DAD ONLINE
|
||
ata-CT1000BX500SSD1_2530E9C74CD1 ONLINE
|
||
ata-CT1000BX500SSD1_2539E9CB84F8 ONLINE
|
||
errors: No known data errors</pre>
|
||
<p class="note">If any drive shows FAULTED or DEGRADED, do not proceed. See ZFS Troubleshooting at the bottom of this document.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">14</span> Confirm backup files are accessible â NAS08</div>
|
||
<pre>sudo zfs list
|
||
ls /kingdezignsnas/
|
||
ls /kingdezignsnas/Backups/NAS08/
|
||
|
||
# List archives sorted newest first:
|
||
ls -lht /kingdezignsnas/Backups/NAS08/nas08-backup-*.tar.gz</pre>
|
||
<p class="note">Use <code>/kingdezignsnas/</code> directly at this stage. The <code>/export/kingdezigns-all/</code> path is recreated by OMV after the config restore in Phase 6.</p>
|
||
</div>
|
||
|
||
<!-- PHASE 5 -->
|
||
<div class="phase-label">Phase 5 â Extract the Backup Archive</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">15</span> Create restore working directory â NAS08</div>
|
||
<pre>sudo mkdir -p /tmp/nas08-restore</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">16</span> Extract the most recent backup â NAS08</div>
|
||
<pre># Replace YYYY-MM-DD with the actual archive date from step 14
|
||
sudo tar -xzf /kingdezignsnas/Backups/NAS08/nas08-backup-YYYY-MM-DD.tar.gz \
|
||
-C /tmp/nas08-restore</pre>
|
||
<p class="note">This takes a few minutes â the archive is ~1.4GB. Wait for the prompt to return.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">17</span> Confirm extraction and read the manifest â NAS08</div>
|
||
<pre># Confirm all folders are present:
|
||
ls /tmp/nas08-restore/
|
||
ls /tmp/nas08-restore/nas08-backup-*/
|
||
|
||
# Read the manifest:
|
||
cat /tmp/nas08-restore/nas08-backup-*/MANIFEST.txt</pre>
|
||
<p>You should see: <code>omv-config/</code> <code>etc/</code> <code>docker-compose/</code> <code>app-data/</code> <code>cron/</code> <code>MANIFEST.txt</code></p>
|
||
</div>
|
||
|
||
<!-- PHASE 6 -->
|
||
<div class="phase-label">Phase 6 â Restore System Configuration</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">18</span> Restore /etc â NAS08</div>
|
||
<pre>sudo rsync -a /tmp/nas08-restore/nas08-backup-*/etc/ /etc/</pre>
|
||
<p class="note">Restores all system configuration: network settings, nginx, fstab, cron, and everything else under /etc.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">19</span> Restore OMV configuration â NAS08</div>
|
||
<pre># Copy saved OMV config back into place:
|
||
sudo cp /tmp/nas08-restore/nas08-backup-*/omv-config/config.xml.raw \
|
||
/etc/openmediavault/config.xml
|
||
|
||
# Load it into the OMV database:
|
||
sudo omv-confdbadm populate
|
||
|
||
# Re-apply all settings to the system (shares, users, plugins, schedules):
|
||
sudo omv-salt deploy run all</pre>
|
||
<p class="note">The last command takes several minutes. Do not interrupt it â let it complete fully.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">20</span> Reboot and verify OMV â NAS08 then browser</div>
|
||
<pre>sudo reboot</pre>
|
||
<p>After 2â3 minutes, SSH back in and check:</p>
|
||
<pre>ssh rufusking@192.168.150.35</pre>
|
||
<p>Then open a browser on your workstation:</p>
|
||
<pre>http://192.168.150.35</pre>
|
||
<p>Log in and verify: shares, users, and settings are present. Go to <strong>Storage â File Systems</strong> and confirm the ZFS pool is mounted and <code>/export/kingdezigns-all</code> is available.</p>
|
||
</div>
|
||
|
||
<!-- PHASE 7 -->
|
||
<div class="phase-label">Phase 7 â Restore Docker and Application Data</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">21</span> Verify Docker is installed â NAS08</div>
|
||
<pre>sudo docker ps</pre>
|
||
<p>If Docker is not found, install it:</p>
|
||
<pre>sudo apt-get install -y docker-compose-plugin</pre>
|
||
<p>If the OMV Compose plugin is missing, reinstall via OMV UI:</p>
|
||
<pre>OMV UI â System â Plugins â search "compose" â Install</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">22</span> Restore Docker Compose files â NAS08</div>
|
||
<pre>sudo rsync -a /tmp/nas08-restore/nas08-backup-*/docker-compose/ \
|
||
/export/kingdezigns-all/Docker/Compose/
|
||
|
||
# Verify files landed correctly:
|
||
ls /export/kingdezigns-all/Docker/Compose/</pre>
|
||
<p>Expected: <code>nextcloud/</code> <code>nginx-proxy-manager/</code> <code>pihole/</code> <code>plex/</code> <code>vaultwarden/</code> <code>global.env</code></p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">23</span> Restore Pi-hole data â NAS08</div>
|
||
<pre>sudo rsync -a /tmp/nas08-restore/nas08-backup-*/app-data/pihole/ \
|
||
/export/kingdezigns-all/Docker/pihole/</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">24</span> Restore Plex data â NAS08</div>
|
||
<pre>sudo rsync -a /tmp/nas08-restore/nas08-backup-*/app-data/plex/ \
|
||
/export/kingdezigns-all/Docker/plex/</pre>
|
||
<p class="note">This restores your music organization (Media/), library metadata (Metadata/), and watch history (Plug-in Support/). These are the folders that represent weeks of manual work and are the primary reason this backup exists.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">25</span> Restore Shared folder â NAS08</div>
|
||
<pre>sudo rsync -a /tmp/nas08-restore/nas08-backup-*/app-data/shared/ \
|
||
/export/kingdezigns-all/Docker/Shared/</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">26</span> Fix permissions on all Docker folders â NAS08</div>
|
||
<pre>sudo chown -R root:users /export/kingdezigns-all/Docker/
|
||
sudo chmod -R 775 /export/kingdezigns-all/Docker/</pre>
|
||
</div>
|
||
|
||
<!-- PHASE 8 -->
|
||
<div class="phase-label">Phase 8 â Bring Containers Back Up</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">27</span> Start each container one at a time â NAS08</div>
|
||
<pre>cd /export/kingdezigns-all/Docker/Compose/pihole && sudo docker compose up -d
|
||
cd /export/kingdezigns-all/Docker/Compose/nginx-proxy-manager && sudo docker compose up -d
|
||
cd /export/kingdezigns-all/Docker/Compose/vaultwarden && sudo docker compose up -d
|
||
cd /export/kingdezigns-all/Docker/Compose/nextcloud && sudo docker compose up -d
|
||
cd /export/kingdezigns-all/Docker/Compose/plex && sudo docker compose up -d</pre>
|
||
<p class="note">Start one at a time â not all at once â so errors from individual containers are not lost in combined output.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">28</span> Verify all containers are running â NAS08</div>
|
||
<pre>sudo docker ps</pre>
|
||
<p>All containers should show status <strong>Up</strong>. If any show <strong>Exited</strong>, check that container's logs:</p>
|
||
<pre>sudo docker logs pihole
|
||
sudo docker logs nginx-proxy-manager
|
||
sudo docker logs vaultwarden
|
||
sudo docker logs nextcloud
|
||
sudo docker logs plex</pre>
|
||
</div>
|
||
|
||
<!-- PHASE 9 -->
|
||
<div class="phase-label">Phase 9 â Final Steps & Verification</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">29</span> Verify the backup script is in place â NAS08</div>
|
||
<pre>ls -lh /usr/scripts/omv/nas08-backup.sh</pre>
|
||
<p>If missing (it lives outside /etc so may not have restored automatically), copy it from your workstation:</p>
|
||
<pre># On your workstation:
|
||
scp nas08-backup.sh rufusking@192.168.150.35:/usr/scripts/omv/nas08-backup.sh
|
||
|
||
# On NAS08:
|
||
chmod +x /usr/scripts/omv/nas08-backup.sh</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">30</span> Restore the crontab â NAS08</div>
|
||
<pre>sudo crontab /tmp/nas08-restore/nas08-backup-*/cron/root-crontab.txt
|
||
|
||
# Verify it was applied:
|
||
sudo crontab -l</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">31</span> Reschedule the backup job in OMV UI</div>
|
||
<pre>OMV UI â System â Scheduled Jobs â Add
|
||
Enabled: Yes
|
||
Name: NAS08 Backup
|
||
Command: /usr/scripts/omv/nas08-backup.sh
|
||
Minute: 0
|
||
Hour: 2
|
||
Day of month: */3
|
||
Month: *
|
||
Day of week: *
|
||
â Save</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">32</span> Clean up the restore working directory â NAS08</div>
|
||
<pre>sudo rm -rf /tmp/nas08-restore</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">33</span> Run a fresh backup immediately to create a new baseline</div>
|
||
<pre># Option A â OMV UI:
|
||
System â Scheduled Jobs â NAS08 Backup â Run
|
||
|
||
# Option B â SSH on NAS08:
|
||
sudo /usr/scripts/omv/nas08-backup.sh
|
||
|
||
# Verify it completed successfully:
|
||
ls -lh /export/kingdezigns-all/Backups/NAS08/
|
||
cat /export/kingdezigns-all/Backups/NAS08/backup.log</pre>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">34</span> Run a ZFS scrub to verify data integrity â NAS08</div>
|
||
<pre>sudo zpool scrub kingdezignsnas
|
||
|
||
# Check results a few minutes later:
|
||
sudo zpool status</pre>
|
||
<p>A healthy result:</p>
|
||
<pre>scan: scrub repaired 0B in 00:xx:xx with 0 errors</pre>
|
||
</div>
|
||
|
||
<!-- ZFS TROUBLESHOOTING -->
|
||
<div class="phase-label">ZFS Troubleshooting</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">â</span> Pool shows DEGRADED â 1 drive failed</div>
|
||
<pre>sudo zpool status â failed drive shows FAULTED or UNAVAIL</pre>
|
||
<p>RAIDZ2 tolerates up to 2 simultaneous drive failures. The pool is still fully readable and writable. Complete the full restore procedure first, then replace the failed drive. Do not skip recovery steps.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">â</span> Pool shows DEGRADED â 2 drives failed</div>
|
||
<pre>sudo zpool status â two drives show FAULTED or UNAVAIL</pre>
|
||
<p>Data is at risk but still accessible. Complete the restore immediately and replace both drives as fast as possible. Seek assistance before doing anything else if unsure.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">â</span> Pool cannot be imported â not found</div>
|
||
<pre># Scan all connected devices for importable pools:
|
||
sudo zpool import
|
||
|
||
# If kingdezignsnas appears in the list, force import:
|
||
sudo zpool import -f kingdezignsnas</pre>
|
||
<p class="note">If no pools are found at all, PCIe is likely not enabled. Verify Steps 8â9 (boot config) were applied correctly and reboot again before retrying.</p>
|
||
</div>
|
||
|
||
<div class="step">
|
||
<div class="step-header"><span class="step-num">â</span> Pool imported but /export/kingdezigns-all is missing</div>
|
||
<pre># Access backup files directly via the ZFS mount point:
|
||
ls /kingdezignsnas/Backups/NAS08/</pre>
|
||
<p class="note"><code>/export/kingdezigns-all/</code> is created by OMV when it mounts the filesystem. Use <code>/kingdezignsnas/</code> as the base path for all restore commands until OMV is fully restored.</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 6 â LESSONS LEARNED -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="6">Lessons learned</div>
|
||
<table>
|
||
<thead>
|
||
<tr><th>Lesson</th><th>Detail</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>PCIe must be enabled before ZFS can see the drives</td>
|
||
<td>The Raspberry Pi 5 Penta SATA HAT requires PCIe to be explicitly enabled in <code>/boot/firmware/config.txt</code>. On a fresh OS this is never set. Without it all 4 ZFS drives are invisible and the pool cannot be imported.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>ZFS pool import must happen before any restore steps</td>
|
||
<td>Without importing the pool, the backup files are completely inaccessible. It is now Phase 4, immediately after enabling PCIe, before any restore work begins.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Plex Media/ and Metadata/ contain irreplaceable manually curated data</td>
|
||
<td>These folders hold music organization, artwork, and library structure that took weeks of manual work. They must always be included in backups even though they are large (~1.6GB combined).</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Never run scripts that touch OMV without a tested recovery plan first</td>
|
||
<td>A prior script run by another AI tool broke OMV and wiped Plex music organization. This session was motivated by that incident. Backup first, change second.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Custom scripts belong in /usr/scripts/ not /usr/local/bin/</td>
|
||
<td>Writing to /usr/local/bin/ requires root SCP access. A dedicated /usr/scripts/ directory owned by rufusking avoids this and keeps custom scripts separated from system binaries.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Always create output directories before the first script run</td>
|
||
<td>The script failed on first run because /Backups/NAS08/ did not exist. The script now handles this automatically via mkdir -p, but always verify the path exists before the first run on a new system.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vaultwarden icon_cache is large and completely disposable</td>
|
||
<td>The icon cache for website favicons was inflating the archive. It regenerates automatically and must always be excluded.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Recovery documentation must be stored off the device being recovered</td>
|
||
<td>A recovery plan stored only on NAS08 is useless when NAS08 is down. Keep copies on a workstation, USB drive, or in print.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
<!-- SECTION 7 â NETWORK MAP UPDATES -->
|
||
<div class="section">
|
||
<div class="section-title" data-num="7">Network map â items to update</div>
|
||
<table>
|
||
<thead>
|
||
<tr><th>Item</th><th>Old value</th><th>New value</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>NAS08 â Hardware detail</td>
|
||
<td><code>Raspberry Pi</code></td>
|
||
<td><code>Raspberry Pi 5 with Penta SATA HAT</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â Backup status</td>
|
||
<td><code>None</code></td>
|
||
<td><code>Automated â every 3 days â 30-day retention</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â Backup destination</td>
|
||
<td><code>None</code></td>
|
||
<td><code>/export/kingdezigns-all/Backups/NAS08/</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â Backup script</td>
|
||
<td><code>None</code></td>
|
||
<td><code>/usr/scripts/omv/nas08-backup.sh</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â ZFS pool name</td>
|
||
<td><code>not documented</code></td>
|
||
<td><code>kingdezignsnas (RAIDZ2, 4 Ã 1TB SSD)</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â ZFS mount point</td>
|
||
<td><code>not documented</code></td>
|
||
<td><code>/kingdezignsnas</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â PCIe requirement</td>
|
||
<td><code>not documented</code></td>
|
||
<td><code>dtparam=pciex1 + dtparam=pciex1_gen=3 required in /boot/firmware/config.txt on fresh OS</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>NAS08 â Recovery plan</td>
|
||
<td><code>None</code></td>
|
||
<td><code>NAS08-Backup-Summary.html â 34 steps across 9 phases + ZFS troubleshooting</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
<div class="footer">
|
||
<span>KingDezigns Home Network</span>
|
||
<span>Generated 2026-05-11</span>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|