sudo apt update
sudo apt install p7zip-full
mkdir -p ~/.config/backup
nano ~/.config/backup/backup.pass
# type your password on a single line, save, exit
chmod 600 ~/.config/backup/backup.pass
Verify it’s locked down:
ls -l ~/.config/backup/backup.pass
# should show: -rw------- 1 youruser youruser
Open backup-projects.sh and set the three paths at the top:
SOURCE_DIR — your actual Projects folder pathDEST_DIR — the backup folder on your secondary drivePASSFILE — path to the password file from step 2chmod +x backup-projects.sh
sudo mv backup-projects.sh /usr/local/bin/backup-projects.sh
sudo -u YOURUSER /usr/local/bin/backup-projects.sh
Check that the .7z file shows up in DEST_DIR, and confirm you can open it:
7z t /mnt/secondary/Backups/projects-<date>.7z
# it will prompt for the password — test that it actually decrypts
Edit your user’s crontab (NOT root’s, so it has normal access to your home dir/password file):
crontab -e
Add this line to run nightly at 1:00 AM:
0 1 * * * /usr/local/bin/backup-projects.sh >> /home/YOURUSER/backup-projects.log 2>&1
MEGA’s sync client only watches the folders you’ve explicitly added (Desktop, Documents, Downloads, Music, Pictures per your setup). Your secondary drive backup folder isn’t in that list by default, so you have two options:
/mnt/secondary/Backups as an additional synced folder.ln -s /mnt/secondary/Backups ~/Documents/ProjectBackups
Because the script uses -mhe=on (encrypted headers — file names are hidden, not just contents), GUI archive tools generally can’t open this file. GNOME Archive Manager, the 7-Zip desktop app, etc. will either error out or show an empty/unreadable listing. This is expected and not a sign of corruption. Always use the 7z command line for this archive.
Verify integrity (no extraction, just checks the archive is intact):
7z t /mnt/secondary/Backups/projects-<date>.7z
You’ll be prompted for the password. A healthy archive ends with Everything is Ok.
Extract / restore to a scratch folder:
7z x /mnt/secondary/Backups/projects-<date>.7z -o/tmp/restore-test
Prompts for the password, then extracts the full folder tree into /tmp/restore-test.
Extract to the original location (overwrites in place — be careful):
7z x /mnt/secondary/Backups/projects-<date>.7z -o/home/YOURUSER/Projects -aoa
-aoa = overwrite all existing files without asking. Omit it if you want to be prompted per file instead.
The “test restores periodically” note below used to be a manual chore. It is now automated by verify-backups.sh, which runs the drill every week and alerts if anything is wrong. It is strictly read-only — it never creates, deletes, or modifies a backup, only tests the newest one.
Each run, in order (any hard failure → red Discord alert + exit 1):
7z t integrity test passes — real CRC decompression of every file, using the password;Projects/shared_trading_lib) actually exists inside — catches the nasty case where the archive is intact but doesn’t contain the real tree (e.g. backed up the wrong or an empty folder).A soft yellow warning (still passes) fires if the newest archive shrank more than 50% versus the previous one — an early signal that the backup started capturing less than it should.
/home/aztechguy/.local/bin/verify-backups.sh (in user bin, not /usr/local/bin, so it runs as your user with normal access to the password file — same reasoning as running the backup from your crontab, not root’s)/home/aztechguy/backup-verify.log--always-notify so a green “backups verified” confirmation lands in Discord once a week (so you know it ran, not just silence). Runs without the flag are silent unless something fails.verify-backups.sh --always-notify/media/aztechguy/2a3a6a06-.../projects-backup/, not the /mnt/secondary/Backups placeholder used in the examples above.Alerts are split across three Discord channels so a backup failure never hides in trading noise. All three URLs live in one place: ~/.config/discord-webhooks/ (dir 700, files 600).
| File | Feeds | How the consumer reads it |
|---|---|---|
stocks.txt |
all trading bots (options, DIA, QQQ, 0DTE, Portfolio 2), plus daily check-in, watchdog, fill logger | the bots read discord_webhook from their own state.json; stocks.txt is the source of truth, synced to those files by apply-stocks-webhook.sh |
calendar.txt |
run-buffer-automation.sh (Sniffspot/Swimply calendar) |
symlink to ~/.config/backup/discord_webhook.txt, which that script already reads live each run |
backups.txt |
verify-backups.sh |
read live each run (falls back to the stocks webhook only if the file is missing, so a config slip never silences a real alert) |
To rotate a webhook:
backups.txt, or calendar.txt). Read live next run — no other step.stocks.txt, then run apply-stocks-webhook.sh to push it into all five trading state.json files (it backs up each one first). Run it outside market hours (not 6 AM-1 PM PT weekdays) so it doesn’t race a bot writing its own state.Current segregation: stocks and calendar each already have their own webhook; backups gets a dedicated one (paste it into backups.txt). Helper scripts live in ~/.local/bin/.
7z t (or 7z x) command above and confirm it actually opens.RETENTION_DAYS in both scripts, raised from 7/14 on 2026-08-09).
The limit was never disk space — ~144 MB per Projects archive is ~4 GB for a month against
665 GB free. It’s detection lag: 7 days only protects you from problems you notice within
a week, and if a file is silently corrupted then faithfully backed up for eight nights, every
copy you hold is already bad. Problems on this box have surfaced far later than a week (a
509-day-old ext4 error; automation scripts unbacked-up for a month).find -mtime +7 counts whole 24-hour blocks and
truncates, and the sweep runs after the new archive is built — so a file sitting within
minutes of the boundary survives an extra night and is swept the next one. Expect “N or N+1”,
not exactly N.~/backup-projects.log) will grow forever; if that bothers you, add logrotate config or just periodically truncate it.