- Posted on
- • Artificial Intelligence
Artificial Intelligence ZFS Administration
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence ZFS Administration: Smarter Bash for Safer Pools
Your ZFS pool is healthy—until it isn’t. Disks quietly accumulate reallocated sectors, write latencies creep up, and free space dwindles faster than you expect. What if an assistant could continuously read your ZFS and S.M.A.R.T. telemetry, forecast capacity crunches, and suggest safe, shell-ready actions during incidents? In this post, we’ll build an AI-assisted ZFS admin toolkit using familiar Bash, a little Python, and a local LLM you can run offline.
Value in a nutshell:
Catch issues earlier with trend forecasts (e.g., “pool will hit 80% in 12.3 days”).
Triage incidents faster with AI-generated checklists and shell commands.
Generate snapshot retention policies that match data churn and space constraints.
Keep everything auditable, reviewable, and under your control.
Why AI + ZFS now?
ZFS is rich in signals (zpool status/iostat, zpool events, S.M.A.R.T., dmesg). Humans miss patterns; machines don’t.
Local LLMs can summarize complex state and produce actionable, non-destructive steps—without sending data off-host.
Light-weight forecasting gives you time to add vdevs, rebalance, or prune snapshots before performance degrades.
What we’ll build
1) A telemetry collector that logs pool and device health in JSON lines. 2) A simple capacity-forecast tool that estimates time to 80% full for each pool. 3) An AI-powered triage command that digests system outputs and prints a clean, shell-ready plan. 4) A snapshot policy assistant that proposes retention tiers (hourly/daily/weekly/monthly) tailored to your datasets.
All scripts are plain Bash (plus one tiny Python file), designed to be reviewed and adapted.
Prerequisites and installation
Note: This guide assumes you already have ZFS installed and your pools imported. We’ll install supporting tools only.
Base tools:
jq, curl
smartmontools, nvme-cli
sysstat (for iostat)
bc
Python 3 with NumPy/Pandas/Scikit-learn (for capacity forecasting)
Optional: Ollama for running a local LLM
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y jq curl smartmontools nvme-cli sysstat bc python3 python3-pip
# Optional: install Python scientific stack from distro
sudo apt install -y python3-numpy python3-pandas python3-sklearn
# Or via pip (user install):
python3 -m pip install --user numpy pandas scikit-learn
Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y jq curl smartmontools nvme-cli sysstat bc python3 python3-pip
# Optional: Python scientific stack from distro
sudo dnf install -y python3-numpy python3-pandas python3-scikit-learn
# Or via pip (user install):
python3 -m pip install --user numpy pandas scikit-learn
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y jq curl smartmontools nvme-cli sysstat bc python3 python3-pip
# Optional: Python scientific stack from distro
sudo zypper install -y python3-numpy python3-pandas python3-scikit-learn
# Or via pip (user install):
python3 -m pip install --user numpy pandas scikit-learn
Optional: Install Ollama (local LLM runtime; works offline once model is downloaded)
curl -fsSL https://ollama.com/install.sh | sh
# Pull a compact, capable model (adjust to taste)
ollama pull llama3:8b
1) Collect ZFS telemetry to JSON (cron-friendly)
Create /usr/local/sbin/zfs-ai-collect.sh:
#!/usr/bin/env bash
set -euo pipefail
OUT_DIR=${OUT_DIR:-/var/log/zfs-ai}
mkdir -p "$OUT_DIR"
chmod 750 "$OUT_DIR"
timestamp() { date -u +%s; }
collect_pool() {
local pool="$1" ts size alloc free cap frag health
ts=$(timestamp)
read -r size alloc free cap frag health < <(zpool list -Hp -o size,allocated,free,capacity,fragmentation,health "$pool")
# Grab aggregate ops and bandwidth (best-effort, version-dependent)
read_ops=0 write_ops=0 read_bps=0 write_bps=0
mapfile -t io < <(zpool iostat -Hpvy 1 1 "$pool" 2>/dev/null | awk 'NR==1, /capacity/ {next} /^[^ \t]/ {root=$1} $1==root {ro=$3; wo=$4; rb=$5; wb=$6; print ro,wo,rb,wb; exit}')
if [[ ${#io[@]} -gt 0 ]]; then
read read_ops write_ops read_bps write_bps <<<"${io[0]}"
fi
# Devices in the pool (for quick S.M.A.R.T. health summaries)
mapfile -t devs < <(zpool status -P "$pool" | awk '/^\t(\/dev|scsi|nvme)/{gsub("\t",""); print $1}' | sort -u)
smart_bad=0
for d in "${devs[@]}"; do
[[ -e "$d" ]] || continue
if smartctl -H -n standby "$d" >/tmp/smh 2>&1; then
grep -q "PASSED" /tmp/smh || smart_bad=$((smart_bad+1))
fi
done
jq -nc --arg ts "$(timestamp)" \
--arg pool "$pool" \
--argjson size "$size" \
--argjson alloc "$alloc" \
--argjson free "$free" \
--arg cap "$cap" \
--arg frag "$frag" \
--arg health "$health" \
--argjson ro "$read_ops" \
--argjson wo "$write_ops" \
--argjson rb "$read_bps" \
--argjson wb "$write_bps" \
--argjson smart_bad "$smart_bad" \
'$ARGS.named' >> "$OUT_DIR/metrics.jsonl"
}
main() {
mapfile -t pools < <(zpool list -H -o name 2>/dev/null || true)
for p in "${pools[@]}"; do
collect_pool "$p"
done
}
main
Make it executable:
sudo install -m 0750 zfs-ai-collect.sh /usr/local/sbin/zfs-ai-collect.sh
Add a cron job to run every 10 minutes:
sudo tee /etc/cron.d/zfs-ai <<'CRON'
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
OUT_DIR=/var/log/zfs-ai
*/10 * * * * root /usr/local/sbin/zfs-ai-collect.sh
CRON
Check logs:
tail -n 3 /var/log/zfs-ai/metrics.jsonl | jq .
2) Forecast time-to-80% capacity (simple regression)
Create /usr/local/sbin/zfs-ai-forecast.py:
#!/usr/bin/env python3
import sys, json, time, math
from collections import defaultdict
data = defaultdict(list)
for line in sys.stdin:
try:
rec = json.loads(line)
pool = rec["pool"]
ts = int(rec["ts"])
size = float(rec["size"])
alloc = float(rec["alloc"])
data[pool].append((ts, size, alloc))
except Exception:
continue
def eta_to_target(tseries, target):
if len(tseries) < 5:
return None
xs = [t for t,_,_ in tseries]
ys = [a for _,_,a in tseries]
x0 = xs[0]
xs = [x - x0 for x in xs]
n = len(xs)
meanx = sum(xs)/n
meany = sum(ys)/n
num = sum((xs[i]-meanx)*(ys[i]-meany) for i in range(n))
den = sum((xs[i]-meanx)**2 for i in range(n))
if den == 0: return None
slope = num/den # bytes/sec
if slope <= 0: return math.inf
y0 = ys[-1]
return (target - y0)/slope
for pool, points in data.items():
points.sort()
size = points[-1][1]
used = points[-1][2]
target = 0.80 * size
secs = eta_to_target(points, target)
if secs is None:
print(f"{pool}: not enough data (need >= 5 points).")
continue
if secs == math.inf:
print(f"{pool}: usage stable/decreasing; no projected 80% fill.")
continue
days = secs / 86400
pct = 100 * used / size
print(f"{pool}: {pct:.1f}% used now; projected 80% in {days:.1f} days.")
Install and run:
sudo install -m 0755 zfs-ai-forecast.py /usr/local/sbin/zfs-ai-forecast.py
cat /var/log/zfs-ai/metrics.jsonl | /usr/local/sbin/zfs-ai-forecast.py
Tip: Pipe this into your alerting if the forecast drops below a threshold (e.g., <14 days).
3) AI-assisted incident triage (local LLM via Ollama)
Create /usr/local/sbin/zfs-ai-triage.sh:
#!/usr/bin/env bash
set -euo pipefail
MODEL="${OLLAMA_MODEL:-llama3:8b}"
PROMPT_FILE=$(mktemp)
cleanup(){ rm -f "$PROMPT_FILE"; }
trap cleanup EXIT
gather() {
echo "=== zpool status ==="
zpool status -x || true
echo
echo "=== zpool list ==="
zpool list -v || true
echo
echo "=== zfs list (top 20 by used) ==="
zfs list -t filesystem -o name,used,avail,referenced,compressratio -S used | head -n 21 || true
echo
echo "=== recent zpool events (last 50) ==="
zpool events -v -T u 2>/dev/null | tail -n 200 || true
echo
echo "=== smartctl summaries ==="
for d in $(zpool status -P | awk '/^\t(\/dev|scsi|nvme)/{gsub("\t",""); print $1}' | sort -u); do
echo "# $d"
smartctl -H -A -n standby "$d" 2>&1 | egrep -i 'overall|reallocated|pending|crc|media_errors|wear|percentage_used|temperature' || true
echo
done
echo "=== dmesg (zfs,sd,nvme errors, 200 lines) ==="
dmesg -T | egrep -i 'ZFS|pool|sd .*error|nvme.*err|checksum|I/O error' | tail -n 200 || true
}
cat > "$PROMPT_FILE" <<'P'
You are an expert SRE specializing in ZFS on Linux. Analyze the following system outputs.
- Identify likely root causes.
- Classify risk: low/medium/high, and whether immediate action is needed.
- List the next 3–7 safe, non-destructive commands to run for confirmation.
- If replacement/resilver is needed, show the exact zpool/zfs commands, but mark them as "REQUIRES CONFIRMATION".
- Output concise bullet points and shell-ready commands only.
Context:
P
gather >> "$PROMPT_FILE"
if command -v ollama >/dev/null 2>&1; then
echo "Using Ollama model: $MODEL" >&2
ollama run "$MODEL" < "$PROMPT_FILE"
else
echo "No local LLM found (ollama). Install Ollama or set up your own inference endpoint." >&2
exit 1
fi
Use it when you see trouble:
sudo OLLAMA_MODEL=llama3:8b /usr/local/sbin/zfs-ai-triage.sh | less -R
You’ll get a short list of probable causes, risk levels, and safe commands (e.g., inspect a suspect vdev, scrub planning, or resilver checklist). Always review before acting.
4) Snapshot policy assistant (recommendations you can apply)
Create /usr/local/sbin/zfs-ai-snapshot-policy.sh:
#!/usr/bin/env bash
set -euo pipefail
TOP="${1:-}"
[ -z "$TOP" ] && { echo "Usage: $0 <pool|dataset>"; exit 1; }
report=$(zfs list -r -o name,used,avail,referenced,compressratio,logicalused "$TOP" | sed '1d' | head -n 100)
read -r -d '' SYSTEM <<'SYS' || true
You are assisting with ZFS snapshot policy planning.
Constraints:
- Prioritize datasets with high change rate (approximate by logicalused/used) and low available space.
- Propose a tiered policy like: hourly (N), daily (N), weekly (N), monthly (N).
- Keep total snapshot space under ~30% of used unless ample free space exists.
- Output for each dataset: "dataset: hourly=X daily=Y weekly=Z monthly=W".
SYS
PROMPT="$SYSTEM
Here is current state (columns: name used avail referenced compressratio logicalused):
$report
Produce recommendations for the top 20 datasets only. Keep it short."
if command -v ollama >/dev/null 2>&1; then
ollama run "${OLLAMA_MODEL:-llama3:8b}" <<< "$PROMPT"
else
echo "Ollama not found; printing a safe default for top 20:"
echo "$report" | awk 'NR<=20 {print $1": hourly=24 daily=14 weekly=8 monthly=6"}'
fi
Example: apply an hourly snapshot for 24 hours to one dataset (review first!)
DATASET=tank/home
# Create snapshot with ISO-like tag
zfs snapshot -r "$DATASET@auto-$(date -u +%Y%m%d-%H%MZ)"
# Prune hourly snapshots older than 24h for that dataset (non-recursive example)
zfs list -t snapshot -o name -s creation | grep "^$DATASET@auto-" | \
awk -v cutoff="$(date -u -d '24 hours ago' +%s)" '
{
split($0, a, "@auto-");
gsub("Z","",a[2]); gsub("-","",a[2]); # naive parse
}
' # implement your preferred pruning logic or use tools like sanoid
For production-grade snapshot orchestration and pruning, consider purpose-built tools (e.g., sanoid/syncoid) and use the AI output as planning input.
Real-world-style example
Telemetry shows pool “tank” at 73.2% with rising write_ops/read_ops and 1 device with S.M.A.R.T. not PASSED.
Forecast says: “tank: 73.2% used now; projected 80% in 9.8 days.”
Triage assistant outputs:
- Likely cause: one device with pending sectors; vdev experiencing retries; medium risk; plan a scrub and device replacement ASAP.
- Safe checks:
zpool status -v tanksmartctl -x /dev/sdXzpool iostat -v tank 2 10- Replacement (REQUIRES CONFIRMATION):
zpool offline tank /dev/sdX- replace drive physically
zpool replace tank /dev/sdXzpool status tank(monitor resilver)
Meanwhile, snapshot policy assistant proposes tightening hourly snapshots (higher churn datasets) and trimming weekly retention—buying space while you schedule replacement and capacity expansion.
Safety checklist
Never run destructive commands automatically. Require human review or an explicit ALLOW flag.
Test scripts on a non-production host or a VM with a throwaway pool.
Keep your LLM local (Ollama) if logs or hostnames are sensitive.
Version-control your prompts and outputs for traceability.
Complement with monitoring/alerting (Prometheus node_exporter + textfile collector, or simple cron + mail).
Conclusion and next steps
AI won’t replace your ZFS expertise—but it will make it faster to see trends, triage incidents, and plan safe actions. Start by:
Scheduling the telemetry collector (done).
Running the forecast weekly; alert when T–80% < 14 days.
Using triage during scrubs, checksum errors, or rising latencies.
Letting the snapshot assistant propose retention tiers, then implementing with your preferred tooling.
Next steps:
Export metrics to Prometheus (textfile) and graph forecasts in Grafana.
Add arcstats and per-dataset change tracking for richer signals.
Extend the triage prompt with site-specific runbooks.
If you found this useful, wire it into your cron/alert flow this week. Your future self (and your pools) will thank you.