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

推荐订阅源

Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
H
Hacker News: Front Page
Stack Overflow Blog
Stack Overflow Blog
B
Blog
I
InfoQ
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
月光博客
月光博客
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
F
Full Disclosure
Hacker News - Newest:
Hacker News - Newest: "LLM"
Recent Announcements
Recent Announcements
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
T
Tailwind CSS Blog
S
Secure Thoughts
P
Privacy International News Feed
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 最新话题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
Cybersecurity and Infrastructure Security Agency CISA
Last Week in AI
Last Week in AI
W
WeLiveSecurity
Google Online Security Blog
Google Online Security Blog
P
Privacy & Cybersecurity Law Blog
D
DataBreaches.Net
Engineering at Meta
Engineering at Meta
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Project Zero
Project Zero
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
博客园 - Franky
阮一峰的网络日志
阮一峰的网络日志
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
H
Help Net Security
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
博客园 - 【当耐特】

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 Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Linux Backup Made Simple: Automate Incremental System Snapshots with Restic
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.