## Summary - Add `doc-card-titles` mise task to enumerate all doc cards by title/id and detect duplicates - Remove redundant aliases from zk cards where alias matched the id - Rename `reference/storage/postgresql.md` title to "PostgreSQL Storage" to avoid duplicate with `reference/services/postgresql.md` - Convert all 175 path-based wiki-links `[[reference/path|Title]]` to title-based `[[Title]]` format - Add pre-commit hook to check for duplicate card titles on doc changes ## Deployment and Testing - [x] Pre-commit hooks pass - [x] `mise run doc-card-titles` shows no duplicates - [ ] Verify wiki-links work correctly in Quartz build 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/89
6.7 KiB
| id | aliases | tags | ||
|---|---|---|---|---|
| borgmatic |
|
|
Borgmatic Management Log
Borgmatic runs daily backups from Indri to Sifaka NAS using Borg backup.
Service Details
- Installed via: mise (pipx)
- Config:
~/.config/borgmatic/config.yaml(ansible-managed) - Schedule: Daily at 2:00 AM via LaunchAgent
- Repository:
/Volumes/backups/borg/on Sifaka
What Gets Backed Up
Directories:
~/code/personal/zk- Zettelkasten (primary)/opt/homebrew/var/forgejo- Git forge data~/.config/borgmatic- Borgmatic config itself~/Documents- Personal documents~/Pictures- Photos (see note below)
Note on iCloud Photos: macOS Photos.app defaults to "Optimize Mac Storage" which keeps only thumbnails locally. Borgmatic only backs up what's on disk, so iCloud-only photos are NOT backed up. If you need full photo backups via borgmatic, either disable "Optimize Mac Storage" in Photos preferences, or use a tool like osxphotos which forces downloads. See log entry 2026-01-28.
Databases:
minifluxPostgreSQL database on k8s CloudNativePG cluster (pg.ops.eblu.me)teslamatePostgreSQL database on k8s CloudNativePG cluster (pg.ops.eblu.me)
Not backed up (by design):
- ZIM archives in
~/transmission/- re-downloadable via torrent - Prometheus metrics - ephemeral data
- Loki logs - ephemeral (now in k8s PVC)
- devpi data - in k8s PVC, backup strategy TBD
PostgreSQL Backup
Borgmatic uses native postgresql_databases support to stream pg_dump directly to Borg:
- No intermediate files needed
- Database keeps running (no downtime)
- Consistent transactional snapshots
- Uses
borgmaticuser withpg_read_all_datarole - Password read from
~/.pgpass(managed by borgmatic ansible role) - Uses explicit
pg_dump_commandpath (/opt/homebrew/opt/postgresql@18/bin/pg_dump) since LaunchAgent doesn't have homebrew in PATH - Uses explicit
local_path(/opt/homebrew/bin/borg) for same reason
Databases backed up:
pg.ops.eblu.me:5432/miniflux- CloudNativePG cluster in k8spg.ops.eblu.me:5432/teslamate- CloudNativePG cluster in k8s
Ansible Management
Borgmatic is fully managed via ansible in 1767747119-YCPO:
mise run provision-indri -- --tags borgmatic
The role deploys:
~/.config/borgmatic/config.yaml- Main configuration- LaunchAgent plist for scheduled runs
Useful Commands
# List archives
ssh indri 'mise x -- borgmatic list'
# Extract from latest archive
ssh indri 'mise x -- borgmatic extract --archive latest --path /some/path'
# Run backup manually
ssh indri 'mise x -- borgmatic create --verbosity 1'
# Check repository health
ssh indri 'mise x -- borgmatic check'
Retention Policy
- 7 daily backups
- 12 monthly backups
- 1000 yearly backups (effectively forever)
Monitoring
Borgmatic metrics are collected hourly via a script at ~/bin/borgmatic-metrics and exposed to Prometheus via the node_exporter textfile collector.
View the Grafana dashboard at: https://grafana.tail8d86e.ts.net (select "Borgmatic Backups" dashboard)
Metrics include:
borgmatic_up- repository accessibilityborgmatic_repo_deduplicated_size_bytes- actual disk usageborgmatic_last_archive_original_size_bytes- size of data being backed upborgmatic_last_archive_deduplicated_size_bytes- new data added per backupborgmatic_archive_count- number of archivesborgmatic_last_archive_timestamp- when last backup ran
# Check metrics file
ssh indri 'cat /opt/homebrew/var/node_exporter/textfile/borgmatic.prom'
# Check metrics LaunchAgent status
ssh indri 'launchctl list | grep borgmatic-metrics'
Log
Tue Jan 28 2026
- Investigated massive backup size increase (~69GB deduplicated, ~94GB per archive)
- Root cause: immich-sync role (added Jan 26, removed Jan 28) used osxphotos to export photos
- Lesson learned: osxphotos forces Photos.app to download all iCloud originals locally
- Photos.app defaults to "Optimize Mac Storage" which keeps only thumbnails locally
- Before immich-sync: borgmatic was backing up thumbnails (~few GB)
- After immich-sync: borgmatic now has full 42GB of photo originals
- This is actually a bonus - provides redundant photo backup alongside iCloud and Immich
- Retention policy means these photos will be kept in yearly archives essentially forever
- Future plan: Once Immich (on sifaka "photos" volume with Synology offsite backup) is fully set up, Pictures may be removed from borgmatic as redundant
Thu Jan 23 2026
- Note: Forgejo
app.iniis now managed by ansible (secrets in 1Password) /opt/homebrew/var/forgejostill backed up for git repositories and data- But
app.inirecovery no longer depends on borgmatic (can be regenerated via ansible)
Wed Jan 22 2026
- Removed
/opt/homebrew/var/lokifrom backup sources (stale data from pre-k8s migration) - Loki now runs in k8s with ephemeral storage - logs are not backed up by design
- Verified backup integrity after cleanup
Mon Jan 20 2026 (P5)
- Removed
~/devpifrom backup sources (devpi migrated to k8s) - devpi data now in k8s PVC - backup strategy TBD
Sun Jan 19 2026 (P4)
- Removed localhost PostgreSQL backup (brew pg retired)
- Updated to backup only
pg.tail8d86e.ts.net(k8s CloudNativePG) - Moved .pgpass management from postgresql role to borgmatic role
Sun Jan 19 2026 (P3)
- Fixed borgmatic failing to find
borgbinary by addinglocal_pathoption to config - Added k8s-pg (CloudNativePG cluster) backup alongside brew PostgreSQL
- Added ACL grant for
tag:homelab→tag:k8son port 5432 for backup access - Successfully tested disaster recovery: restored miniflux data from borgmatic dump to k8s-pg
- Created
borgmaticuser in k8s-pg via CloudNativePG managed roles - Both localhost and k8s-pg databases backed up during migration period
Sat Jan 18 2026
- Fixed borgmatic-metrics script failing in LaunchAgent context by using absolute paths (
/opt/homebrew/bin/borg,/opt/homebrew/bin/jq) instead ofmise x -- borg - This was causing the Grafana dashboard to show "Repository Status: DOWN" and missing time series data
Fri Jan 17 2026
- Fixed PostgreSQL backup failure by adding explicit
pg_dump_commandpath (was failing with "pg_dump: command not found") - Removed
~/code/3rd/kiwix-toolsfrom backups (was just symlinks, ZIM archives are re-downloadable) - Enabled Loki log backup (removed from exclude_patterns)
- Added borgmatic_metrics role for Prometheus metrics collection
- Added Grafana dashboard for backup monitoring (size trends, dedup ratio, time since last backup)
Thu Jan 16 2026
- Moved config from manual management to ansible-managed template
- Added
postgresql_databasesbackup for miniflux database - Config now deployed via
ansible/roles/borgmatic/templates/config.yaml.j2