
Obsidi Backup
- 1 repo stars
- Updated June 20, 2026
- cameronsjo/obsidi-backup
Vault backup sidecar with AI commit messages and restic cloud storage
About
obsidi-backup is a Claude Code skill in the Git & Pull Requests category. Vault backup sidecar with AI commit messages and restic cloud storage
- obsidi-backup
- Git & Pull Requests
- AI-coding skill
Obsidi Backup by the numbers
- Data as of Jul 11, 2026 (Skillselion catalog sync)
/plugin marketplace add cameronsjo/obsidi-backup/plugin install obsidi-backup@obsidi-backupAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| repo stars | ★ 1 |
|---|---|
| Last updated | June 20, 2026 |
| Repository | cameronsjo/obsidi-backup ↗ |
What it does
Vault backup sidecar with AI commit messages and restic cloud storage
README.md
obsidi-backup
A lightweight sidecar container that automatically backs up your Obsidian vault with git versioning and cloud storage via restic.
Why?
Obsidian Sync is great, but it's not a backup. This container provides:
- Git history - Every change tracked, browse history, diff versions
- Cloud backup - Encrypted, deduplicated backups to Azure, S3, B2, or any restic backend
- AI commit messages - Optional Claude-powered summaries of what changed
- Notifications - Know when backups succeed or fail
Features
- File watching - Real-time monitoring with configurable debounce
- Git versioning - Auto-commits with AI-generated or timestamp messages
- Cloud backup - Restic to Azure, S3, B2, SFTP, or local storage
- Health endpoint - HTTP
/healthfor monitoring and orchestration - Configurable retention - Customize daily, weekly, monthly snapshots
- Notifications - Discord, Slack, or generic webhooks
- Dry run mode - Test configuration without making changes
- Multi-arch - Runs on amd64 and arm64 (Raspberry Pi, Apple Silicon)
Quick Start
services:
vault-backup:
image: ghcr.io/cameronsjo/obsidi-backup:latest
environment:
TZ: America/Chicago
# Storage backend (Azure example)
AZURE_ACCOUNT_NAME: mystorageaccount
AZURE_ACCOUNT_KEY: ${AZURE_ACCOUNT_KEY}
RESTIC_REPOSITORY: azure:obsidian-backup:/vault
RESTIC_PASSWORD: ${RESTIC_PASSWORD}
# Optional: AI commit messages
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
# Optional: Notifications
DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL}
volumes:
- /path/to/your/vault:/vault
ports:
- "8080:8080"
After starting, initialize the restic repository:
docker exec vault-backup restic init
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Obsidian Vault Backup │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ │
│ │ Watchdog│───▶│ Debounce │───▶│ Git │───▶│ Restic │ │
│ │ Monitor │ │ (5 min) │ │ Commit │ │ Backup │ │
│ └─────────┘ └──────────┘ └────────┘ └─────────┘ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ LLM │ │ │
│ │ │ (Haiku) │ │ │
│ │ └──────────┘ │ │
│ │ ▼ │
│ │ ┌──────────┐ ┌───────────┐ │
│ └────────▶│ Health │ │ Notify │ │
│ │ Server │ │ (Discord) │ │
│ └──────────┘ └───────────┘ │
│ │ │
└──────────────────────┼──────────────────────────────────────┘
▼
:8080/health
Flow:
- Watchdog monitors the vault for file changes
- Debounce waits for 5 minutes of inactivity (configurable)
- Git commit stages all changes, generates commit message (AI or timestamp)
- Restic backup encrypts and uploads to cloud storage
- Prune removes old snapshots per retention policy
- Notify sends success/failure alerts
Configuration
Storage Backends
Restic supports many backends. Set RESTIC_REPOSITORY accordingly:
| Backend | Repository Format | Additional Env Vars |
|---|---|---|
| Azure Blob | azure:container:/path |
AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY |
| AWS S3 | s3:s3.amazonaws.com/bucket |
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
| Backblaze B2 | b2:bucket:/path |
B2_ACCOUNT_ID, B2_ACCOUNT_KEY |
| SFTP | sftp:user@host:/path |
SSH key or RESTIC_PASSWORD |
| Local | /path/to/backup |
- |
| REST Server | rest:http://host:8000/ |
- |
See restic documentation for all options.
Environment Variables
Required
| Variable | Description |
|---|---|
RESTIC_REPOSITORY |
Restic repository URL |
RESTIC_PASSWORD |
Restic encryption password |
Plus backend-specific variables (see table above).
Paths & Timing
| Variable | Default | Description |
|---|---|---|
VAULT_PATH |
/vault |
Path to Obsidian vault |
DEBOUNCE_SECONDS |
300 |
Wait time after last change (5 min) |
HEALTH_PORT |
8080 |
Health endpoint port |
Git
| Variable | Default | Description |
|---|---|---|
GIT_USER_NAME |
Obsidian Backup |
Commit author name |
GIT_USER_EMAIL |
backup@local |
Commit author email |
AI Commit Messages
Uses Claude Haiku 4.5 to generate meaningful commit messages from changed filenames.
| Variable | Default | Description |
|---|---|---|
ANTHROPIC_API_KEY |
- | Anthropic API key |
ANTHROPIC_MODEL |
claude-haiku-4-5-20251001 |
Model to use |
Or use any OpenAI-compatible API:
| Variable | Default | Description |
|---|---|---|
LLM_API_URL |
- | API endpoint URL |
LLM_API_KEY |
- | API key |
LLM_MODEL |
anthropic/claude-haiku-4.5 |
Model identifier |
Retention Policy
| Variable | Default | Description |
|---|---|---|
RETENTION_DAILY |
7 |
Daily snapshots to keep |
RETENTION_WEEKLY |
4 |
Weekly snapshots to keep |
RETENTION_MONTHLY |
12 |
Monthly snapshots to keep |
Notifications
| Variable | Default | Description |
|---|---|---|
NOTIFY_LEVEL |
all |
all, errors, success, or none |
DISCORD_WEBHOOK_URL |
- | Discord webhook |
SLACK_WEBHOOK_URL |
- | Slack incoming webhook |
WEBHOOK_URL |
- | Generic webhook (JSON POST) |
Feature Flags
| Variable | Default | Description |
|---|---|---|
DRY_RUN |
false |
Test mode - no commits or backups |
PIPELINE_STALE_THRESHOLD_SECONDS |
7200 |
Seconds of watcher silence before sync_pipeline_status goes stale |
Health Endpoints
/health — liveness probe
curl http://localhost:8080/health
{
"status": "healthy",
"uptime_seconds": 3600
}
Thin liveness wrapper. status is unhealthy when there are pending changes and no backup in 24+ hours. Safe to use as a Kubernetes/Docker liveness probe.
/status — full pipeline signal
curl http://localhost:8080/status
{
"status": "healthy",
"sync_pipeline_status": "healthy",
"last_watcher_event_at": "2024-01-15T10:30:00Z",
"last_change_detected_at": "2024-01-14T09:12:44Z",
"last_commit_at": "2024-01-14T09:17:44Z",
"last_push_at": "2024-01-14T09:17:46Z",
"last_restic_snapshot_at": "2024-01-14T09:17:51Z",
"pipeline_stale_threshold_seconds": 7200,
"seconds_since_watcher_event": 412,
"pending_changes": false,
"uptime_seconds": 3600,
"upstream_heartbeat": null
}
sync_pipeline_status detects when the upstream sync process (e.g. obsidi-headless) has gone silent, even if no commit-worthy changes have arrived:
healthy— watcher has seen filesystem activity within the threshold windowstale— no filesystem activity for longer thanPIPELINE_STALE_THRESHOLD_SECONDS(default 2h). The upstream writer has likely stopped.unknown— service just started, no events recorded yet
The key insight: even when the vault is content-quiet, obsidi-headless's ob sync --continuous writes ephemeral metadata files (.obsidian/workspace.json, lockfiles) at sub-hour cadence. If watcher events stop arriving, the sync process has almost certainly crashed — not just "nothing to sync."
Gatus monitoring condition
- name: obsidi-backup-pipeline
url: http://obsidi-backup:8080/status
interval: 5m
conditions:
- "[STATUS] == 200"
- "[BODY].sync_pipeline_status == healthy"
Status values:
healthy- Everything working normallyunhealthy- Changes pending but no backup in 24+ hours
Notifications
Discord
Create a webhook in Discord (Server Settings → Integrations → Webhooks):
DISCORD_WEBHOOK_URL: https://discord.com/api/webhooks/123456/abcdef
Slack
Create an Incoming Webhook in Slack:
SLACK_WEBHOOK_URL: https://hooks.slack.com/services/T.../B.../...
Generic Webhook
For Ntfy, Gotify, Home Assistant, n8n, or any HTTP endpoint:
WEBHOOK_URL: https://your-service.com/webhook
Payload format:
{
"title": "Vault Backup Complete",
"message": "Committed and backed up: 3 files changed",
"status": "success",
"timestamp": "2024-01-15T10:30:00Z"
}
Restore
List available snapshots:
docker exec vault-backup restic snapshots
Restore the latest snapshot:
docker exec vault-backup restic restore latest --target /restore
Restore a specific snapshot:
docker exec vault-backup restic restore abc123 --target /restore
Mount snapshots as a filesystem (for browsing):
docker exec -it vault-backup restic mount /mnt
Troubleshooting
"Restic repository not initialized"
Run restic init after first start:
docker exec vault-backup restic init
"Vault directory is not writable"
The container needs write access to create git commits. Remove :ro from your volume mount:
volumes:
- /path/to/vault:/vault # Not /path/to/vault:/vault:ro
No backups happening
- Check if debounce period elapsed (default 5 minutes)
- Verify health endpoint:
curl http://localhost:8080/health - Check container logs:
docker logs vault-backup
AI commit messages not working
- Verify
ANTHROPIC_API_KEYis set correctly - Check logs for API errors
- Fallback timestamp messages will be used if AI fails
Notifications not sending
- Verify webhook URL is correct
- Check
NOTIFY_LEVELsetting - Test webhook manually with curl
Development
# Install dependencies
pip install -e ".[dev]"
# Run locally
VAULT_PATH=/path/to/vault python -m vault_backup
# Run tests
pytest
# Lint
ruff check src/
License
PolyForm Noncommercial 1.0.0. Commercial use requires a separate license — get in touch