nas16-scripts/zfs/NAS16-zfs_pools.html

511 lines
17 KiB
HTML
Raw Normal View History

2026-07-26 21:21:03 -04:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KingDezigns — NAS16 ZFS Pool Maintenance Scripts</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 — NAS16 ZFS Pool Maintenance Scripts</h1>
<div class="meta">
<span>📅 2026-05-12</span>
<span>🖥 NAS16 / Raspberry Pi 5</span>
<span>⚙️ OpenMediaVault (OMV)</span>
<span>🌐 Apache · PHP · MariaDB · Webmin</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>NAS16 serves live websites via Apache/PHP and hosts their databases in MariaDB. A software or hardware failure — corrupt SD card, bad OMV update, runaway script — would take down all hosted sites and lose database state with no documented recovery path and no saved configuration.</p>
<div class="badge-row">
<span class="badge amber">ZFS Scrubbing</span>
<span class="badge amber">ZFS Reporting</span>
</div>
<p>The goal is to create regular maintenance to the ZFS pools for this NAS. This is done to protect and keep the pools healthy.</p>
<div class="callout amber" style="margin-top:1rem;">
<div class="clue-label">⚠ Critical — store this document off NAS16</div>
<p>Keep a copy of this document on your workstation, a USB drive, or in print. If NAS16 is down you cannot read files stored on it.</p>
</div>
</div>
<!-- SECTION 2 — Script to Create ZFS report -->
<div class="section">
<div class="section-title" data-num="2">Script to Create ZFS report</div>
<div class="step">
<div class="step-header"><span class="step-num">1</span> Create the folders on NAS16</div>
<p class="note">This script gets the current ZFS pool status and reports it back to the administrator via email. Use these commands to setup the scripts on the NAS.</p>
<pre>ssh rufusking@192.168.150.40
sudo mkdir /usr/scripts/zfs
sudo nano nas16_zfs_report.sh</pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">2</span> Copy the content of the entire nas16_zfs_report.sh script and paste it in the terminal window. Save (CTRL + X) and commit (Y) the changes</div>
<pre><a href="nas16_zfs_report.sh">nas16_zfs_report.sh</a></pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">3</span> Make the script executable — NAS16</div>
<pre>chmod +x /usr/scripts/zfs/nas16_zfs_report.sh</pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">4</span> Run a test report — NAS16</div>
<pre>sudo /usr/scripts/zfs/nas16_zfs_report.sh</pre>
<p>Watch for the report in your inbox</p>
</div>
</div>
<!-- SECTION 3 — Script to perform a ZFS pool scrub -->
<div class="section">
<div class="section-title" data-num="3">Script to perform a ZFS pool scrub</div>
<div class="step">
<div class="step-header"><span class="step-num">1</span> Create the folders on NAS16</div>
<p class="note">This script initiates a ZFS pool scrub and reports back the status to the administrator via email. Use these commands to setup this script on the NAS.</p>
<pre>ssh rufusking@192.168.150.40
sudo mkdir /usr/scripts/zfs
sudo nano nas16_zfs_scrub.sh</pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">2</span> Copy the content of the entire nas16_zfs_scrub.sh script and paste it in the terminal window. Save (CTRL + X) and commit (Y) the changes</div>
<pre><a href="nas16_zfs_scrub.sh">nas16_zfs_scrub.sh</a></pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">3</span> Make the script executable — NAS16</div>
<pre>chmod +x /usr/scripts/zfs/nas16_zfs_scrub.sh</pre>
</div>
<div class="step">
<div class="step-header"><span class="step-num">4</span> Run a test report — NAS16</div>
<pre>sudo /usr/scripts/zfs/nas16_zfs_report.sh</pre>
<p>Watch for the report in your inbox</p>
</div>
</div>
<!-- SECTION 4 — KEY DIFFERENCES FROM NAS08 -->
<div class="section">
<div class="section-title" data-num="4">Key differences from NAS08</div>
<table>
<thead>
<tr><th>Area</th><th>NAS08</th><th>NAS16</th></tr>
</thead>
<tbody>
<tr>
<td>Primary role</td>
<td>Docker containers (Pi-hole, Plex, Vaultwarden, Nextcloud, NPM)</td>
<td>Web server (Apache/PHP), databases (MariaDB), Webmin admin</td>
</tr>
<tr>
<td>What's backed up beyond /etc + OMV</td>
<td>Docker Compose files, Pi-hole data, Plex config/metadata</td>
<td>Apache vhosts, PHP config, MariaDB dumps, Webmin config</td>
</tr>
<tr>
<td>Website files</td>
<td>N/A</td>
<td>Intentionally excluded — stored on NAS drives, covered by redundancy</td>
</tr>
<tr>
<td>Database backup</td>
<td>None (no databases)</td>
<td>Full mysqldump of all user databases, .sql.gz per database</td>
</tr>
<tr>
<td>Backup destination</td>
<td>/export/kingdezigns-all/Backups/NAS08/</td>
<td>/export/kingdezignsnas-16/Backups/NAS16/</td>
</tr>
<tr>
<td>ZFS pool name</td>
<td>kingdezignsnas</td>
<td>kingdezignsnas-16 <em>(assumed — verify with <code>sudo zpool list</code>)</em></td>
</tr>
<tr>
<td>Recovery phases</td>
<td>9 phases, 34 steps</td>
<td>10 phases, 40 steps</td>
</tr>
</tbody>
</table>
</div>
<!-- SECTION 5 — NOTES & ASSUMPTIONS -->
<div class="section">
<div class="section-title" data-num="5">Notes &amp; assumptions to verify</div>
<div class="callout amber">
<div class="clue-label">⚠ Verify these before first run</div>
<p>The script was written based on information provided and follows Raspberry Pi OS / Debian conventions. Verify the following on NAS16 before treating any backup as production-ready.</p>
</div>
<table>
<thead>
<tr><th>Item</th><th>Assumed value</th><th>How to verify</th></tr>
</thead>
<tbody>
<tr>
<td>ZFS pool name</td>
<td><code>kingdezignsnas-16</code></td>
<td><code>sudo zpool list</code></td>
</tr>
<tr>
<td>Backup destination path</td>
<td><code>/export/kingdezignsnas-16/Backups/NAS16/</code></td>
<td><code>ls /export/</code> — confirm share name matches</td>
</tr>
<tr>
<td>MariaDB auth method</td>
<td>Uses <code>/etc/mysql/debian.cnf</code> (maintenance account, no password when run as root)</td>
<td>Run: <code>sudo mysql --defaults-file=/etc/mysql/debian.cnf -e "SHOW DATABASES;"</code> — should work without password</td>
</tr>
<tr>
<td>PHP version(s)</td>
<td>Auto-detected from <code>/etc/php/*/</code></td>
<td><code>php -v</code> and <code>ls /etc/php/</code></td>
</tr>
<tr>
<td>Apache config location</td>
<td><code>/etc/apache2/</code> (standard Debian)</td>
<td><code>apache2 -V</code> — confirms config path</td>
</tr>
<tr>
<td>Webmin config location</td>
<td><code>/etc/webmin/</code></td>
<td><code>ls /etc/webmin/</code></td>
</tr>
</tbody>
</table>
</div>
<!-- SECTION 6 — NETWORK MAP UPDATES -->
<div class="section">
<div class="section-title" data-num="6">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>NAS16 — Backup status</td>
<td><code>None</code></td>
<td><code>Automated — every 3 days — 30-day retention</code></td>
</tr>
<tr>
<td>NAS16 — Backup destination</td>
<td><code>None</code></td>
<td><code>/export/kingdezignsnas-16/Backups/NAS16/</code></td>
</tr>
<tr>
<td>NAS16 — Backup script</td>
<td><code>None</code></td>
<td><code>/usr/scripts/omv/nas16-backup.sh</code></td>
</tr>
<tr>
<td>NAS16 — 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>NAS16 — Web stack</td>
<td><code>not documented</code></td>
<td><code>Apache · PHP · MariaDB · Webmin · Adminer</code></td>
</tr>
<tr>
<td>NAS16 — Recovery plan</td>
<td><code>None</code></td>
<td><code>NAS16-Backup-Summary.html — 40 steps across 10 phases + ZFS troubleshooting</code></td>
</tr>
</tbody>
</table>
</div>
<div class="footer">
<span>KingDezigns Home Network</span>
<span>Generated 2026-05-12</span>
</div>
</div>
</body>
</html>