Automating the boring half of my backup process
Overview
My critical file backup process has a lot of steps, and most of them are exactly the kind of thing that’s easy to skip when you’re doing them from memory once a month: export the Drive folder, export three spreadsheets, export contacts, then remember to actually check that each file showed up and isn’t empty. backup_process automates the parts that are purely mechanical and leaves an interactive checklist for the parts — password manager exports, KOReader settings, Pleco settings — that genuinely need me at the keyboard.
What it does
monthly_backup.py is the single entry point:
- Runs the Google export automatically: a Drive folder as a ZIP, three Sheets as
.odsfiles, and all Contacts as acontacts.vcf - Verifies every expected file exists and is non-empty
- Walks through an interactive checklist for the rest of the process, letting me confirm, skip, or flag each item as failed
Everything lands directly in ~/Documents, where Pika Backup on my encrypted USB drive picks it up.
Why it’s built this way
Checks its own work. The automated step doesn’t just run and hope — check_expected_files() and check_contacts_vcf() verify each output file actually exists and has content before the checklist even starts. If the Contacts export silently returned zero contacts, I’d rather find out from the script than three months later when I need to restore from a backup that isn’t there.
Open, portable formats over proprietary ones. Sheets export to .ods instead of .xlsx, and contacts export to VCard 3.0 instead of Google’s CSV. Both were chosen so the backup is readable by something other than the exact tool that created it — the entire point of a backup is that it outlives the ecosystem it came from.
No timestamped folders. Every run overwrites the same files in ~/Documents rather than creating a new dated subdirectory. That looks like it throws away history, but it doesn’t — Pika Backup already versions everything in ~/Documents on the encrypted drive, so building a second versioning scheme on top would just be redundant complexity with more places for something to go stale.
Read-only scopes, always. The Google OAuth scopes are drive.readonly and contacts.readonly. A backup script has no reason to ever be able to write to the account it’s backing up, so it isn’t given the option.
A gotcha worth knowing
token.json holds a live OAuth refresh token — anyone with that file has ongoing read access to the Drive and Contacts scopes until it’s revoked. It’s gitignored alongside credentials.json, but gitignoring something only prevents future commits; it doesn’t help if either file was ever committed before the ignore rule existed. If that ever happens, the fix isn’t deleting the commit — it’s revoking the credential in the Google Cloud Console immediately, since the file may already be sitting in a clone or a cache somewhere.
Try it yourself
You’ll need Python 3.10+ and a Google Cloud project with the Drive and People APIs enabled. Full OAuth setup is in the README; the rest of the manual checklist — password manager, Pika Backups, encrypting the drive itself — is covered in my full backup guide.
codeberg.org/splitsubdued/backup_process
Related
- Critical file backup process guide — the full manual process this script partially automates
- Automating my monthly net worth tracker
- Synthesizing Obsidian periodic notes with Claude Code

