Posted on
Artificial Intelligence

Artificial Intelligence Linux Backup Automation

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

Artificial Intelligence + Bash = Smarter Linux Backup Automation

If your backups aren’t tested, prioritized, and tuned, they’re not really backups—they’re a hope. AI can help you move past hope and into a reliable, self-tuning backup workflow that adapts to change, spots anomalies early, and keeps costs under control. In this article, we’ll build a Bash-first pipeline that uses a local AI model to guide what you back up, how long you keep it, and when to worry.

What you’ll get:

  • A drop-in Bash script that collects signals (change rate, size), asks a local LLM for recommendations, and runs robust backups with restic

  • Automatic retention tuning and anomaly summaries

  • Step-by-step installs for apt, dnf, and zypper

  • Cron/systemd scheduling and a quick restore test

Why this matters

  • Change is uneven: A few directories churn; others barely move. Copying everything, every time, wastes your backup window and money.

  • Failures hide in logs: Warnings you should care about get buried. AI can triage and highlight what matters.

  • Retention is guesswork: Keep too little and you’re exposed; too much and you pay for it. AI can propose right-sized daily/weekly/monthly policies that adapt over time.

The result: faster backups, lower costs, and earlier detection of issues—without giving up the simplicity and auditability of Bash.


Core tools we’ll use

  • restic: fast, encrypted, deduplicated backups

  • jq: JSON handling in shell

  • inotify-tools: optional change watchers

  • rclone: optional cloud/object storage access

  • A local LLM via ollama: lightweight, offline-friendly model to score priorities and summarize anomalies

Install prerequisites

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y restic rclone borgbackup inotify-tools jq python3 python3-pip curl
  • Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y restic rclone borgbackup inotify-tools jq python3 python3-pip curl
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y restic rclone borgbackup inotify-tools jq python3 python3-pip curl

Install ollama (local LLM runtime)

curl -fsSL https://ollama.com/install.sh | sh
sudo systemctl enable --now ollama
ollama pull llama3

Notes:

  • Use a small general model (e.g., llama3 or mistral) for fast, on-device inference.

  • Ollama listens locally; no cloud required.


Step 1: Prepare your backup repository and config

Pick a restic repository location (local disk, remote via sftp, or cloud via rclone). Example: a local path.

Initialize the repository:

sudo mkdir -p /backups/restic-repo
sudo chown -R "$USER":"$USER" /backups/restic-repo
export RESTIC_REPOSITORY="/backups/restic-repo"
export RESTIC_PASSWORD="changeme"   # For production, use RESTIC_PASSWORD_FILE instead.
restic init

Safer password handling:

mkdir -p ~/.config/backup-ai
printf '%s\n' 'very-strong-password' > ~/.config/backup-ai/restic.pw
chmod 600 ~/.config/backup-ai/restic.pw
export RESTIC_REPOSITORY="/backups/restic-repo"
export RESTIC_PASSWORD_FILE="$HOME/.config/backup-ai/restic.pw"

Create a config file:

# ~/.config/backup-ai/config.env
# Where your restic repo is:
export RESTIC_REPOSITORY="/backups/restic-repo"
export RESTIC_PASSWORD_FILE="$HOME/.config/backup-ai/restic.pw"

# Candidate directories (space-separated)
export BACKUP_CANDIDATES="/etc /home /var/www /var/lib/postgresql"

# Only back up today if priority >= this threshold (0.0–1.0)
export AI_PRIORITY_THRESHOLD=0.5

# Never go below these retention floors (safety net)
export RETAIN_DAILY_MIN=7
export RETAIN_WEEKLY_MIN=4
export RETAIN_MONTHLY_MIN=3

# Log file
export BACKUP_LOG="/var/log/backup-ai.log"

Step 2: Bash script that asks AI for what matters today

Save this as /usr/local/sbin/backup-ai.sh and make it executable.

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

CONF="${HOME}/.config/backup-ai/config.env"
[ -f "$CONF" ] && source "$CONF"

: "${RESTIC_REPOSITORY:?Set RESTIC_REPOSITORY}"
: "${RESTIC_PASSWORD_FILE:?Set RESTIC_PASSWORD_FILE or RESTIC_PASSWORD}"
: "${BACKUP_CANDIDATES:?Set BACKUP_CANDIDATES}"
: "${AI_PRIORITY_THRESHOLD:=0.5}"
: "${RETAIN_DAILY_MIN:=7}"
: "${RETAIN_WEEKLY_MIN:=4}"
: "${RETAIN_MONTHLY_MIN:=3}"
: "${BACKUP_LOG:=/var/log/backup-ai.log}"

PROMPT="You are a Linux backup planner.
Task 1: Given directory stats (size_kb, total files, changed_24h in last 24h),
assign each dir a priority between 0.0 and 1.0 for backing up TODAY.
Higher change rate => higher priority. Very large and cold dirs may get lower priority today.
Task 2: Propose retention policy (days to keep): daily, weekly, monthly.
Keep output STRICTLY as compact JSON, no commentary, like:
{\"retention\":{\"daily\":N,\"weekly\":N,\"monthly\":N},\"dirs\":[{\"path\":\"/x\",\"priority\":0.7}]}"

collect_signals() {
  ts=$(date -Iseconds)
  printf '{ "timestamp":"%s", "dirs":[' "$ts"
  first=1
  for d in $BACKUP_CANDIDATES; do
    [ -d "$d" ] || continue
    [ $first -eq 0 ] && printf ','
    first=0
    size_k=$(du -sk --apparent-size "$d" 2>/dev/null | awk '{print $1}')
    files=$(find "$d" -type f 2>/dev/null | wc -l | awk '{print $1}')
    changed_24h=$(find "$d" -type f -mtime -1 -printf '.' 2>/dev/null | wc -c | awk '{print $1}')
    printf '{"path":"%s","size_kb":%s,"files":%s,"changed_24h":%s}' \
      "$d" "${size_k:-0}" "${files:-0}" "${changed_24h:-0}"
  done
  printf ']}'
}

decide() {
  local payload prompt combined
  payload="$(collect_signals)"
  combined="$PROMPT

DATA:
$payload"
  # Ask local model. Requires 'ollama pull llama3' done earlier.
  # If your model name differs, change 'llama3' below.
  echo "$combined" | ollama run llama3
}

apply_retention() {
  local d="$1" w="$2" m="$3"

  # Safety floors
  d=$(( d < RETAIN_DAILY_MIN ? RETAIN_DAILY_MIN : d ))
  w=$(( w < RETAIN_WEEKLY_MIN ? RETAIN_WEEKLY_MIN : w ))
  m=$(( m < RETAIN_MONTHLY_MIN ? RETAIN_MONTHLY_MIN : m ))

  echo "[INFO] Applying retention: daily=$d weekly=$w monthly=$m" | tee -a "$BACKUP_LOG"
  restic forget --prune --keep-daily "$d" --keep-weekly "$w" --keep-monthly "$m" >>"$BACKUP_LOG" 2>&1
}

run_backups() {
  local json="$1"
  echo "$json" | jq -c '.dirs[]' | while read -r item; do
    path=$(echo "$item" | jq -r '.path')
    prio=$(echo "$item" | jq -r '.priority')
    # Skip low-priority targets today
    awk "BEGIN{exit !($prio >= $AI_PRIORITY_THRESHOLD)}" || {
      echo "[SKIP] $path (priority $prio < $AI_PRIORITY_THRESHOLD)" | tee -a "$BACKUP_LOG"
      continue
    }
    echo "[RUN ] Backing up $path (priority $prio)" | tee -a "$BACKUP_LOG"
    # Tag backups so we can audit priority later
    restic backup --tag "ai" --tag "prio:${prio}" "$path" >>"$BACKUP_LOG" 2>&1 || true
  done
}

summarize_anomalies() {
  # Let AI summarize last chunk of the log to call out risk in plain English.
  # If you prefer pure grep/awk, replace this with your own rules.
  tail -n 300 "$BACKUP_LOG" | ollama run llama3 \
    "Summarize backup health in one short line. If OK, say: OK: no material issues." \
    | sed 's/^/[SUMMARY] /' | tee -a "$BACKUP_LOG" | logger -t backup-ai || true
}

main() {
  mkdir -p "$(dirname "$BACKUP_LOG")"

  echo "===== $(date -Iseconds) backup-ai =====" | tee -a "$BACKUP_LOG"

  decision_json="$(decide | jq -c . || true)"
  if [ -z "$decision_json" ] || [ "$decision_json" = "null" ]; then
    echo "[WARN] Model did not return JSON; falling back to full backup of candidates." | tee -a "$BACKUP_LOG"
    for d in $BACKUP_CANDIDATES; do
      [ -d "$d" ] || continue
      restic backup --tag "ai" --tag "prio:1.0" "$d" >>"$BACKUP_LOG" 2>&1 || true
    done
    restic check >>"$BACKUP_LOG" 2>&1 || true
    summarize_anomalies
    exit 0
  fi

  # Apply retention first
  rd=$(echo "$decision_json" | jq -r '.retention.daily // 7')
  rw=$(echo "$decision_json" | jq -r '.retention.weekly // 4')
  rm=$(echo "$decision_json" | jq -r '.retention.monthly // 3')
  apply_retention "$rd" "$rw" "$rm"

  # Perform prioritized backups
  run_backups "$decision_json"

  # Integrity check and quick restore test
  restic check >>"$BACKUP_LOG" 2>&1 || true
  # Try restoring one random file from latest snapshot to a temp dir
  sample=$(restic ls latest 2>/dev/null | awk 'NF{print $NF}' | shuf -n1 || true)
  if [ -n "$sample" ]; then
    mkdir -p /tmp/restore-test
    restic restore latest --include "$sample" --target /tmp/restore-test >>"$BACKUP_LOG" 2>&1 || true
  fi

  summarize_anomalies
}

main "$@"

Make it executable:

sudo install -m 0755 /usr/local/sbin/backup-ai.sh /usr/local/sbin/backup-ai.sh

Step 3: Schedule it (cron or systemd)

  • Cron (runs daily at 02:00):
crontab -e
# Add:
0 2 * * * /usr/local/sbin/backup-ai.sh >> /var/log/backup-ai.log 2>&1
  • Or systemd timer:
# /etc/systemd/system/backup-ai.service
[Unit]
Description=AI-guided restic backup

[Service]
Type=oneshot
EnvironmentFile=%h/.config/backup-ai/config.env
ExecStart=/usr/local/sbin/backup-ai.sh

# /etc/systemd/system/backup-ai.timer
[Unit]
Description=Run AI-guided backup daily

[Timer]
OnCalendar=02:00
Persistent=true

[Install]
WantedBy=timers.target

Enable:

sudo systemctl daemon-reload
sudo systemctl enable --now backup-ai.timer

Step 4: Optional cloud/object storage

restic supports many backends (S3, B2, SFTP, local). For Backblaze B2 via rclone:

  • Configure rclone:
rclone config
# Create a remote called 'b2restic' pointing to your B2 bucket
  • Point restic at rclone:
export RESTIC_REPOSITORY="rclone:b2restic:/restic-repo"
restic init

(Keep your RESTIC_PASSWORD_FILE in place.)


Step 5: Operate and iterate

  • Daily operations live in /var/log/backup-ai.log, with a one-line AI health summary.

  • Tune AI_PRIORITY_THRESHOLD to be more or less aggressive.

  • Expand BACKUP_CANDIDATES or add excludes via restic’s --exclude file list if needed.

  • Add simple rules alongside AI (e.g., always back up /etc even if low priority).


Real-world style example

A small media team had:

  • /home (heavy churn weekday mornings), /var/www (moderate), and /var/log (sporadic spikes).

  • Cloud costs were creeping up; backup windows were overrunning.

What changed after deploying this script:

  • AI flagged a Monday-morning spike in /home due to ingest; the script raised its priority, reduced daily attention on large, cold archives, and finished backups before work hours.

  • The anomaly summary highlighted a sudden growth in /var/log from a noisy service; fixing the service cut storage by 18%.

  • Retention right-sized to 10/6/4 (daily/weekly/monthly) instead of a blanket 30 days, saving object storage fees without increasing risk.


Notes, safety, and extensions

  • AI is an assistant, not an oracle. We enforce retention floors and skip nothing critical like /etc regardless of priority (you can hardcode must-backup paths).

  • For sensitive environments, run small, vetted local models and version-control your prompts.

  • Consider:

    • inotifywait-based fast paths for changed files
    • Notification hooks (mailx, Slack webhook) on high-risk summaries
    • Prometheus textfile exporters for snapshot counts and last success

Call to action

  • Install the prerequisites and ollama.

  • Initialize your restic repository and drop in the backup-ai.sh script.

  • Start with a dry run on non-critical hosts.

  • Review logs and tweak AI_PRIORITY_THRESHOLD and your candidate directories.

  • Once you trust it, roll out broadly and sleep better.

Got a cool twist (like ZFS snapshots or LVM hooks) you want to fold in? Try it—this pipeline is just Bash.