Posted on
Artificial Intelligence

Artificial Intelligence Home Backup Systems

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Home Backup Systems (for Bash-first Linux users)

What if your NAS woke up encrypted and your “backup” dutifully synchronized the damage? That’s the nightmare. The opportunity: layer AI-style anomaly detection and intelligent guards around a rock‑solid, Bash-driven backup so it resists user error, ransomware, and silent data rot.

In this post you’ll:

  • Build a reliable, encrypted, deduplicated baseline with restic + rclone

  • Add an AI anomaly detector to spot suspicious backup deltas

  • Install a write‑storm watchdog to pause backups during a likely ransomware event

  • Automate quick restore drills so you can trust your backups

All examples are shell-first. Every tool includes apt, dnf, and zypper install commands.


Why AI for home backups?

  • Backups are only as good as what they capture. If a ransomware event or runaway script mutates thousands of files, a naïve backup may hurry to preserve the damage.

  • Most backup tools don’t “understand” what’s normal for your home dataset. An anomaly layer can spot weird patterns (huge spikes in changed files, bytes added, or duration) and halt or alert before bad data spreads.

  • With a tiny bit of machine learning on top of standard Linux tooling, you can get early-warning signals and safer automation—without buying a black-box appliance.


What you’ll build

1) A restic-based, encrypted backup to local and/or cloud targets (via rclone) 2) A Python-based anomaly detector (IsolationForest) that learns your normal backup patterns and flags spikes 3) An inotify-based write-rate watchdog that can freeze backups during suspicious write storms 4) A restore drill that randomly verifies files and checksums


1) Baseline: encrypted, deduplicated backups with restic + rclone

restic is fast, encrypted, deduplicated, and script-friendly. rclone gives you cloud targets.

Install:

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y restic rclone
  • Fedora/RHEL/CentOS (dnf):
sudo dnf install -y restic rclone
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y restic rclone

Optional: set up a cloud remote with rclone (S3, Backblaze B2, etc.):

rclone config

Example repo targets:

  • Local disk: /mnt/backup/restic

  • Cloud via rclone: rclone:myremote:home-restic

Create a password file and secure it:

mkdir -p ~/.config/restic
chmod 700 ~/.config/restic
echo "choose-a-long-unique-passphrase" > ~/.config/restic/password
chmod 600 ~/.config/restic/password

Initialize the repository:

export RESTIC_PASSWORD_FILE=$HOME/.config/restic/password
export RESTIC_REPOSITORY=/mnt/backup/restic   # or "rclone:myremote:home-restic"
restic init

Create an exclude file:

mkdir -p ~/.config/restic
cat > ~/.config/restic/excludes <<'EOF'
*.tmp
*.cache
node_modules/
.target/
.DS_Store
Thumbs.db
/lost+found
/mnt/*
/proc/*
/sys/*
/dev/*
/run/*
/var/tmp/*
EOF

A practical backup script (with JSON logging for AI analysis):

#!/usr/bin/env bash
set -euo pipefail

# --- config ---
export RESTIC_PASSWORD_FILE="${HOME}/.config/restic/password"
export RESTIC_REPOSITORY="${RESTIC_REPOSITORY:-/mnt/backup/restic}" # override via env if using rclone
BACKUP_SOURCES=(
  "$HOME/Documents"
  "$HOME/Pictures"
  "/etc"
)
EXCLUDES="${HOME}/.config/restic/excludes"
LOGDIR="/var/log/restic-ai"
FREEZE_FILE="/var/run/backup.freeze"
RETENTION_ARGS="--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
HOSTTAG="$(hostname -s)"

# --- prep ---
sudo mkdir -p "${LOGDIR}"
sudo chown "$(id -u)":"$(id -g)" "${LOGDIR}"

if [[ -f "${FREEZE_FILE}" ]]; then
  echo "[backup] freeze file present (${FREEZE_FILE}); skipping backup."
  exit 0
fi

ts=$(date -u +%Y%m%dT%H%M%SZ)
logjson="${LOGDIR}/backup-${HOSTTAG}-${ts}.json"

# --- run backup (JSON) ---
restic backup \
  --json \
  --hostname "${HOSTTAG}" \
  --tag "auto" \
  --exclude-file "${EXCLUDES}" \
  "${BACKUP_SOURCES[@]}" | tee "${logjson}"

# --- AI anomaly check (optional; see section 2) ---
if command -v python3 >/dev/null 2>&1; then
  if python3 /usr/local/bin/ai_anomaly_guard.py --logdir "${LOGDIR}" ; then
    echo "[backup] anomaly guard: normal"
  else
    echo "[backup] anomaly guard: SUSPICIOUS; aborting retention to avoid propagating damage."
    exit 1
  fi
fi

# --- retention/prune ---
restic forget ${RETENTION_ARGS} --prune

Make it executable and test:

chmod +x ~/backup.sh
RESTIC_REPOSITORY=/mnt/backup/restic ~/backup.sh

Automate via cron (daily at 03:30):

crontab -e
# add:
30 3 * * * RESTIC_REPOSITORY=/mnt/backup/restic /home/youruser/backup.sh >> /var/log/backup.cron 2>&1

Or a systemd timer if you prefer (not shown to keep this post focused).


2) AI anomaly detection on backup deltas

We’ll train a simple IsolationForest on your backup history (from restic’s JSON summaries). It learns “normal” change patterns and flags spikes that could imply ransomware or accidental mass edits.

Install Python and pip:

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y python3 python3-pip
  • Fedora/RHEL/CentOS (dnf):
sudo dnf install -y python3 python3-pip
  • openSUSE (zypper):
sudo zypper install -y python3 python3-pip

Install Python libs (user-local is fine):

pip3 install --user pandas scikit-learn

Create the anomaly script at /usr/local/bin/ai_anomaly_guard.py:

#!/usr/bin/env python3
import argparse, json, os, glob, sys
import pandas as pd
from sklearn.ensemble import IsolationForest

def extract_summary(json_path):
    # restic emits multi-line JSON; summary lines include "message_type":"summary"
    rows = []
    with open(json_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            line=line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
            except Exception:
                continue
            if isinstance(obj, dict) and obj.get("message_type") == "summary":
                # Known fields (version dependent). Fallbacks if missing.
                s = obj.get("summary", obj)  # sometimes fields may be top-level
                files_new = s.get("files_new", 0)
                files_changed = s.get("files_changed", 0)
                files_unmodified = s.get("files_unmodified", 0)
                dirs_new = s.get("dirs_new", 0)
                data_added = s.get("data_added", s.get("bytes_added", 0))
                total_bytes_processed = s.get("total_bytes_processed", 0)
                duration = s.get("total_duration", s.get("duration", 0.0)) or 0.0
                rows.append({
                    "files_new": files_new,
                    "files_changed": files_changed,
                    "files_unmodified": files_unmodified,
                    "dirs_new": dirs_new,
                    "data_added": data_added,
                    "total_bytes_processed": total_bytes_processed,
                    "duration_s": duration,
                    "bps": (total_bytes_processed / duration) if duration else 0.0
                })
    return rows

def load_history(logdir):
    data = []
    for path in sorted(glob.glob(os.path.join(logdir, "backup-*.json"))):
        data.extend(extract_summary(path))
    if not data:
        return pd.DataFrame()
    return pd.DataFrame(data)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--logdir", required=True)
    ap.add_argument("--min-samples", type=int, default=8, help="minimum history to train")
    ap.add_argument("--contamination", type=float, default=0.12, help="expected anomaly rate")
    args = ap.parse_args()

    df = load_history(args.logdir)
    if df.empty or len(df) < args.min_samples:
        # Not enough data to judge; allow backup to proceed.
        print("[ai] insufficient history; passing")
        sys.exit(0)

    features = df[["files_new","files_changed","data_added","total_bytes_processed","bps"]].fillna(0.0)
    clf = IsolationForest(
        n_estimators=200,
        contamination=args.contamination,
        random_state=42
    )
    clf.fit(features.iloc[:-1, :])
    pred = clf.predict(features.iloc[[-1], :])[0]  # 1 normal, -1 anomaly
    score = clf.decision_function(features.iloc[[-1], :])[0]

    last = df.iloc[-1].to_dict()
    msg = f"[ai] last backup: files_new={last['files_new']} changed={last['files_changed']} added={last['data_added']} bps={int(last['bps'])} score={score:.3f}"
    if pred == -1:
        print(msg + " -> ANOMALY")
        # Non-zero exit to signal backup script to stop retention/prune
        sys.exit(1)
    else:
        print(msg + " -> normal")
        sys.exit(0)

if __name__ == "__main__":
    main()

Make it executable:

sudo install -m 0755 ai_anomaly_guard.py /usr/local/bin/ai_anomaly_guard.py

Your backup.sh already calls this after each run. If it flags “SUSPICIOUS,” the script aborts before retention/prune, reducing the chance of overwriting good history with bad.

Tip: After a handful of normal backups (8–10), the detector becomes more useful.


3) Real-time write‑storm watchdog (pause backups during chaos)

If a process begins rewriting thousands of files quickly, let a lightweight watcher raise a freeze flag so your scheduled backup won’t run into the fire.

Install inotify-tools:

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y inotify-tools
  • Fedora/RHEL/CentOS (dnf):
sudo dnf install -y inotify-tools
  • openSUSE (zypper):
sudo zypper install -y inotify-tools

A simple watchdog:

#!/usr/bin/env bash
set -euo pipefail

WATCH_PATHS=("$HOME/Documents" "$HOME/Pictures")
EVENTS="modify,create,delete,move"
WINDOW_SEC=60
THRESHOLD=800           # tune for your dataset
FREEZE_FILE="/var/run/backup.freeze"

declare -a bucket
count=0
start=$(date +%s)

inotifywait -m -r -e ${EVENTS} --format '%e %w%f' "${WATCH_PATHS[@]}" | while read -r _; do
  now=$(date +%s)
  if (( now - start >= WINDOW_SEC )); then
    if (( count >= THRESHOLD )); then
      echo "[watchdog] high event rate (${count}/${WINDOW_SEC}s). Creating freeze file: ${FREEZE_FILE}"
      sudo touch "${FREEZE_FILE}"
    fi
    count=0
    start=$now
  fi
  ((count++))
done
  • Run it as a user service or tmux session.

  • Your backup.sh already checks for /var/run/backup.freeze and exits harmlessly.

  • Clear the freeze after investigating:

sudo rm -f /var/run/backup.freeze

This isn’t “AI,” but it’s a pragmatic guardrail that pairs well with anomaly detection.


4) Trust but verify: automatic restore drills

Backups you haven’t restored are just hopeful copies. This quick drill restores a random file from the latest snapshot and compares checksums.

#!/usr/bin/env bash
set -euo pipefail

export RESTIC_PASSWORD_FILE="${HOME}/.config/restic/password"
export RESTIC_REPOSITORY="${RESTIC_REPOSITORY:-/mnt/backup/restic}"

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

SNAP=$(restic snapshots --json | awk -F'"' '/"short_id"/{print $4}' | tail -n1)
[[ -z "${SNAP}" ]] && { echo "No snapshots found"; exit 1; }

# pick a random file path from the snapshot listing
FILE=$(restic ls "${SNAP}" --json | awk -F'"' '/"path":/ {print $4}' | shuf -n1)

[[ -z "${FILE}" ]] && { echo "No files in snapshot"; exit 1; }

echo "[verify] testing ${FILE} from snapshot ${SNAP}"
# compute original checksum if file still exists locally; otherwise skip source hash
if [[ -f "${FILE}" ]]; then
  SRC_HASH=$(sha256sum "${FILE}" | awk '{print $1}')
else
  SRC_HASH=""
fi

restic restore "${SNAP}" --target "${TMPDIR}" --include "${FILE}" >/dev/null
RESTORED="${TMPDIR}${FILE}"

if [[ -f "${RESTORED}" && -n "${SRC_HASH}" ]]; then
  DST_HASH=$(sha256sum "${RESTORED}" | awk '{print $1}')
  if [[ "${SRC_HASH}" == "${DST_HASH}" ]]; then
    echo "[verify] OK: hashes match"
    exit 0
  else
    echo "[verify] WARNING: hash mismatch"
    exit 2
  fi
else
  echo "[verify] Restored file present: $(test -f "${RESTORED}" && echo yes || echo no)."
  exit 0
fi

Schedule it weekly and alert on non-zero exit via your MTA or monitoring.


Real-world workflow

  • Daily at 03:30: backup.sh runs restic, logs JSON, calls AI guard. If normal, it prunes per retention.

  • Watchdog runs continuously. If a write-storm starts, it creates /var/run/backup.freeze so scheduled backups skip.

  • Weekly: restore drill runs a random-file check to keep you honest.

  • If the AI guard flags an anomaly, investigate before clearing the freeze or allowing the next run.


Tuning tips

  • Start conservative: lower the watchdog THRESHOLD only after observing your normal event rate.

  • IsolationForest contamination around 0.1–0.15 works well to catch spikes without too many false alarms. Adjust as you build history.

  • Keep your password file offsite or in a sealed envelope; without it, your backups are unrecoverable (by design).


Conclusion and next steps

You now have a Bash-first backup that’s:

  • Encrypted and deduplicated (restic)

  • Cloud-capable (rclone)

  • AI‑assisted to detect anomalies (IsolationForest)

  • Guarded against write storms (inotify)

  • Verified by routine restore drills

Next steps:

  • Add an offsite target (e.g., B2, S3, rsync.net via rclone).

  • Expand anomaly features (e.g., file extension entropy, per-directory deltas).

  • Integrate notifications (systemd-notify, desktop notify-send, or mail on failure).

  • Consider air‑gapped weekly snapshots for true last‑resort recovery.

If this helped, try implementing one guard this week—the anomaly detector or the watchdog—and schedule a restore drill. Your future self will thank you.