惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
博客园_首页
M
MIT News - Artificial intelligence
雷峰网
雷峰网
GbyAI
GbyAI
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
S
SegmentFault 最新的问题
H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 【当耐特】
V
Visual Studio Blog
月光博客
月光博客
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
云风的 BLOG
云风的 BLOG
美团技术团队
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
有赞技术团队
有赞技术团队

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Linux Backup Made Simple: Automate Incremental System Sna...
Tao Ran · 2026-06-24 · via DEV Community

A single Bash script, a USB drive, and 30 seconds a day. No cloud. No subscriptions. No excuses.

You have spent months tweaking your Linux environment. The perfect i3 config, the custom kernel parameters, the Docker containers you curated one by one. Then one day — a power surge fries your NVMe. A bad rm -rf. A failed dist-upgrade. And it's all gone.

Most Linux users either don't back up at all, or rely on manual rsync and tar commands they run twice a year. The good news: you can set up fully automated, incremental, encrypted system snapshots with a single script and a USB drive — no cloud subscriptions, no complex configuration, no vendor lock-in.

In this guide I'll show you how to use restic — an open-source backup tool with built-in deduplication, client-side encryption, and snapshot management — paired with a Bash script that handles the real-world edge cases for you: detecting external mounts to avoid wasteful backups, skipping caches that would bloat your repository, and initializing itself on first run.


Why Restic?

Most Linux users fall into one of two camps: they either don't back up at all, or they use rsync / tar because that's what they have always used. Both are risky for different reasons. Here is how restic compares:

Tool Incremental Dedup Encryption Snapshot Mgmt Restore UX
rsync partial no no no manual
tar no no no no manual
duplicity yes no yes basic slow on long chains
borg yes yes yes yes requires borg binary
timeshift yes no no yes GUI-focused, tied to local disk
restic yes yes yes yes single binary, any target

Here is why these differences matter in practice:

True Incremental Backups

Restic chunks your files into content-addressed blobs. When you run a backup, it only uploads chunks it hasn't seen before. The second backup of a 200GB system where you changed three config files? It takes seconds, not hours.

Global Deduplication

Have the same 2GB ISO sitting in three different directories? Restic stores it once. The same Python virtual environment duplicated across five projects? One copy. This matters especially when your USB drive is finite.

Client-Side Encryption

Your backup repository is encrypted before a single byte leaves your machine. Lose the USB drive on a train? Without the password, the data is meaningless noise. Restic uses AES-256 in CTR mode with Poly1305-AES for authentication.

Snapshots, Not Archives

Every backup is a named, timestamped snapshot. You can list them, diff them, mount them as a FUSE filesystem, or restore individual files from any point in time. This is closer to what you would expect from a ZFS snapshot than a tarball.

Single Static Binary

No daemon. No database. No kernel module. Restic is one Go binary. Copy it to any machine and it works. This also makes it trivially deployable in rescue environments — just scp the binary and go.


The Script: Automation with Intelligence

A backup tool is only as good as your willingness to run it. If it requires you to remember which directories to exclude, or mounts to skip, or flags to pass, you will eventually stop running it.

The script tackles four real-world problems:

1. Sensible Exclusions Out of the Box

The script ships with a curated set of exclusions that skip directories you would never want in a system backup:

EXCLUDES=(
    --exclude /proc --exclude /sys --exclude /dev
    --exclude /tmp --exclude /run --exclude /mnt --exclude /media
    --exclude /var/tmp --exclude /lost+found --exclude /.snapshots
    --exclude '/home/*/.cache'
    --exclude '/home/*/.npm'
    --exclude '/home/*/.local/share/Trash'
    --exclude /var/cache/apt/archives
)

A few of these deserve explanation:

  • /home/*/.cache — This is the big one. Browser caches, Go build artifacts, pip wheels, and a thousand other things apps dump here. On a well-used system this can easily be 5–10 GB of data you will never need to restore.
  • /home/*/.npm — Node.js package cache. A single npm install rebuilds it.
  • /home/*/.local/share/Trash — The trash bin. You deleted it once already.
  • /var/cache/apt/archives — Downloaded .deb packages. apt update fetches fresh ones.

Using bash arrays ("${EXCLUDES[@]}") instead of a plain string ensures patterns like /home/*/.cache survive shell expansion intact and are interpreted correctly by restic.

2. Auto-Detect External Mounts

On top of the static exclusion list, the script dynamically detects remote filesystems so you never accidentally pull a NAS into your backup. It parses /proc/mounts and appends matches to the same array:

while read -r _ mp fs_type _; do
    case "$fs_type" in
        cifs|nfs|nfs4|fuse.sshfs|fuse.rclone)
            EXCLUDES+=(--exclude "$mp")
            ;;
    esac
done < /proc/mounts

This means you can mount and unmount NAS shares, add new ones, or switch between SMB and NFS, and the script will never accidentally pull them in. Each detected mount is logged so you can verify what was excluded.

3. First Run vs. Daily Run

The script has exactly two paths through its logic:

  • No repository yet → initialize a new encrypted restic repository on the USB drive, then run the first backup.
  • Repository exists → run an incremental backup. Only changed chunks are transmitted.

No separate init step. No configuration file to maintain. Plug in the USB, run the script, and it figures out what to do.

if [ ! -f "${REPO}/config" ]; then
    restic init --repo "$REPO"
fi
restic backup / --repo "$REPO" "${EXCLUDES[@]}"

4. Format Flexibility

Three modes for different needs:

./backup.sh           # incremental — what you run every day
./backup.sh --dry     # preview what would change, no data written
./backup.sh --full    # force re-read of all files

The --dry mode is particularly useful before a system upgrade — see exactly which files have changed since your last backup. The --full mode is for when you suspect filesystem corruption or bit rot and want a fresh scan of every file.


The Full Script

Copy it, adjust the two variables at the top, and you are ready to go:

#!/bin/bash
# ============================================================
# Restic incremental backup script — local machine
# Usage: ./backup.sh          # incremental backup
#        ./backup.sh --dry    # preview changes
#        ./backup.sh --full   # force full scan
# ============================================================
set -euo pipefail

# ---- Config: customize these two lines ----
USB="/media/$USER/backup-disk"    # USB mount point
REPO="${USB}/my-laptop"           # subdirectory (unique per machine)

TIMESTAMP_FILE="${REPO}/LAST_BACKUP"
LOGFILE="${HOME}/.local/state/backup.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"; }
die()  { log "ERROR: $*"; exit 1; }

mkdir -p "$(dirname "$LOGFILE")"

# ---- Dependencies ----
command -v restic >/dev/null 2>&1 || die "restic not installed. Run: sudo apt install restic"

# ---- USB mount check ----
mountpoint -q "$USB" || die "USB not mounted: $USB"

# ---- Build exclude list ----
EXCLUDES=(
    --exclude /proc --exclude /sys --exclude /dev
    --exclude /tmp --exclude /run --exclude /mnt --exclude /media
    --exclude /var/tmp --exclude /lost+found --exclude /.snapshots
    --exclude '/home/*/.cache'
    --exclude '/home/*/.npm'
    --exclude '/home/*/.local/share/Trash'
    --exclude /var/cache/apt/archives
)

# Auto-detect CIFS / NFS / SSHFS external mounts
while read -r _ mp fs_type _; do
    case "$fs_type" in
        cifs|nfs|nfs4|fuse.sshfs|fuse.rclone)
            EXCLUDES+=(--exclude "$mp")
            log "Excluding external mount: $mp ($fs_type)"
            ;;
    esac
done < /proc/mounts

# ---- Repo init ----
if [ ! -f "${REPO}/config" ]; then
    log "Repo not found, initializing restic repository..."
    restic init --repo "$REPO" || die "Repo init failed"
    log "Repo initialized."
else
    log "Repo exists, running incremental backup."
fi

# ---- Arguments ----
RESTIC_ARGS=(
    --verbose
    --limit-upload 50000
    --limit-download 50000
)

case "${1:-}" in
    --dry|--dry-run)
        log "=== DRY-RUN mode ==="
        restic backup / \
            --repo "$REPO" \
            "${EXCLUDES[@]}" \
            "${RESTIC_ARGS[@]}" \
            --dry-run
        log "Dry-run complete."
        exit 0
        ;;
    --full)
        log "Forcing full scan."
        RESTIC_ARGS+=(--force)
        ;;
esac

# ---- Backup ----
log "Starting backup / → $REPO ..."
restic backup / \
    --repo "$REPO" \
    "${EXCLUDES[@]}" \
    "${RESTIC_ARGS[@]}" \
    2>&1 | tee -a "$LOGFILE"

# ---- Timestamp ----
date '+%Y-%m-%d %H:%M:%S' > "$TIMESTAMP_FILE"
log "Backup complete. Timestamp: $(cat "$TIMESTAMP_FILE")"

# ---- Maintenance reminder ----
SNAP_COUNT=$(restic snapshots --repo "$REPO" 2>/dev/null | grep -c '^[a-f0-9]' || echo "?")
log "Snapshot count: $SNAP_COUNT"
log "Tip: restic forget --repo $REPO --keep-daily 14 --keep-monthly 6 --keep-yearly 5 --prune"


Step-by-Step Setup

Prerequisites

  • A USB drive (preferably SSD) with enough capacity. Format it as ext4 for best performance and to preserve Linux permissions.
  • restic installed on your machine (sudo apt install restic on Debian/Ubuntu).

Step 1: Find Your USB

lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,LABEL

Look for your USB drive in the output. Note the mount point — typically /media/$USER/<label>. Set the USB variable in the script to match.

Step 2: Save the Script

Copy the script above into a file and make it executable:

chmod +x backup.sh

If you have multiple machines, give each a different REPO name (e.g., my-laptop, my-desktop, my-server). This keeps their backups in separate subdirectories on the same USB drive.

Step 3: First Run

./backup.sh

Restic will prompt you to set a password for the repository. Do not lose this password. Write it down. Store it in your password manager. Without it, the backup is unrecoverable. There is no "reset password" button.

The first run will take a while — it reads your entire filesystem. On a 200GB SSD over USB 3.0, expect roughly 20–40 minutes. Subsequent runs take seconds to a few minutes.

Step 4: Verify

restic snapshots --repo "$USB/my-laptop"

You should see your first snapshot, timestamped. Run ./backup.sh again a few hours later and you will see a second snapshot appear. The data stored on disk will barely grow because only changed files were uploaded.

Step 5: Automate (Optional but Recommended)

Add a cron job or systemd timer:

# crontab -e
0 20 * * * /home/$USER/scripts/backup.sh >> /home/$USER/.local/state/backup.log 2>&1

This runs the backup every evening at 8 PM. Since the script uses restic's --limit-upload flag, it won't saturate your USB bandwidth even on slower drives.


Maintenance and Restore

Cleaning Up Old Snapshots

Snapshots accumulate. After a few months, you might have hundreds. Restic's forget policy lets you keep a sensible rotation:

restic forget --repo "$USB/my-laptop" \
    --keep-daily 14 \
    --keep-monthly 6 \
    --keep-yearly 5 \
    --prune

This keeps: every snapshot from the last 14 days, one per month for the last 6 months, and one per year for the last 5 years. Everything else is pruned and the underlying data blobs are garbage-collected.

Restoring Your System

Full system restore to a new disk:

restic restore latest --repo "$USB/my-laptop" --target /mnt/new-disk

Single file restore:

restic restore latest --repo "$USB/my-laptop" --target / --include /home/$USER/.bashrc

Restore from a specific date:

restic snapshots --repo "$USB/my-laptop"              # find the snapshot ID
restic restore <snapshot-id> --repo "$USB/my-laptop" --target /path

Checking Repository Health

Run periodically (monthly is fine):

restic check --repo "$USB/my-laptop"               # quick structural check
restic check --repo "$USB/my-laptop" --read-data   # thorough, reads all data

Mounting Snapshots as a Filesystem

You can browse your snapshots without restoring:

restic mount --repo "$USB/my-laptop" /mnt/restic
ls /mnt/restic/snapshots/

This is invaluable for comparing versions or grabbing a file you deleted last week without doing a full restore.


Summary

You don't need a complex backup strategy. You need one USB drive, one script, and one habit.

Restic gives you enterprise-grade backup features — deduplication, encryption, snapshot management — in a tool that is genuinely simple to use. The script wraps it in automation that handles the real-world edge cases: external mounts, cache directories, first-run initialization, and daily incrementals.

Set it up once. Run it often. The next time your system breaks, you will be back up and running in the time it takes to restore a snapshot, not the time it takes to rebuild from memory.