# Seafile & Immich Backup Strategy Analysis ## When to Use When planning offsite/backup strategy for Seafile (558 GB) or Immich on bandwidth-limited remote PBS, and deciding what to back up vs. what to leave on Ceph-only redundancy. ## Seafile Backup Strategies (6 Options) ### Context - Seafile Pro 13.0.19 MC Docker edition on CT111 - File blocks: `/opt/seafile-data/seafile/seafile-data/storage/blocks/` (intrinsically deduplicated, content-addressable) - Config: `/opt/seafile/.env`, `seafile.conf`, `seahub_settings.py` - DB: MySQL (`seafile_db`, `ccnet_db`, `seahub_db`) — currently in `seafile-mysql` container, migration to Galera planned - Total size: ~558 GB (mostly file blocks) - Ceph redundancy: blocks stored on Ceph EC4+1 (5 HDD OSDs, 1-failure tolerance) ### Option Matrix | # | Strategy | Offsite Volume | Pros | Cons | |---|----------|---------------|------|------| | 1 | **PBS Full (current)** | 558 GB init, then delta | Simple, everything included | Huge for offsite, slow restore | | 2 | **Config+DB only** | <1 GB | Fast daily offsite, trivial restore | Blocks only in Ceph — total Ceph loss = all files gone | | 3 | **SeafGC + PBS exclude blocks** | ~2 GB | Clean, regularly garbage-collected | Blocks only in Ceph, needs GC discipline | | 4 | **Seafile Native Backup** | Variable | Official way, incremental | Needs second Seafile server (not S3) | | 5 | **RBD Snapshot + PBS** | 558 GB init, delta like #1 | Point-in-time consistent | Ceph pool must support snapshots | | 6 | **Blocks on S3 (Pro feature)** | Rootfs ~2 GB offsite, blocks on S3 | Clean separation, Ceph relieved | Migration effort, S3 costs | ### Recommendation - **Short-term**: Option 2 (Config+DB offsite) + regular `seaf-gc.sh`. Blocks are redundant in Ceph EC4+1. For DR against total Ceph loss: Option 4 as second pillar. - **Long-term**: Option 6 (S3 storage backend) — moves blocks out of Ceph entirely, making PBS offsite trivial (~2 GB) and Ceph only hosts config+DB. ## Seafile Pro S3 Storage Backend Seafile Pro supports S3 as **primary storage backend** (not backup target). All file blocks are stored directly on S3 instead of local disk/Ceph. ### Configuration (in `seafile.conf`) ```ini [storage_backend] name = s3 bucket = seafile-data key_id = XXX key = XXX region = eu-central-1 endpoint = https://s3.example.com ``` ### Key Distinctions - **Primary storage** — replaces local/Ceph block storage entirely - **NOT a backup target** — S3 is where live data lives, not a backup copy - **Migration required** — existing blocks must be migrated to S3 - **Ceph relief** — once on S3, Ceph only holds config+DB (~2 GB), making PBS offsite backup trivial and fast ### When to Choose S3 Backend - Ceph capacity is constrained (OSDs near full) - Offsite backup of 558 GB blocks is impractical (bandwidth-limited) - S3 storage is cheaper than maintaining Ceph OSDs - Want clean separation: config+DB on Ceph (small, fast), blocks on S3 (large, cheap) ## Seafile Native Backup (`seaf-backup-cmd`) ### How It Works - Uses Seafile's internal RPC API to synchronize libraries to a **second Seafile server** - Incremental — only changed commits/blocks are transferred - The second server must be a full Seafile server (same version) - Commands: `seaf-backup-cmd status`, `seaf-backup-cmd sync ` ### Limitations - **Needs a second Seafile server** — not a simple file/rsync target - **Not S3-compatible** — target must be Seafile server with storage backend - **Setup complexity** — must configure backup server in `seafile.conf` - **Per-library sync** — no global "backup everything" by default ### Location ```bash # In the seafile container: docker exec seafile /opt/seafile/seafile-server-latest/seahub/scripts/seaf-backup-cmd.sh # Error without proper SEAFILE_DATA_DIR env — needs server running ``` ### When to Choose - Need true DR replica (second running Seafile instance) - Have infrastructure for a second server - Want incremental, deduplicated replication (not full copies) ## Immich Backup Considerations ### Architecture - CT115 (immich): stopped, 20 GB rootfs — upload directory location unclear - CT117 (immich-postgresql): running, 914 MB DB on proxmox6 - Photos/videos are NOT deduplicated (unlike Seafile blocks) ### Strategy Options | # | Strategy | Offsite Volume | Notes | |---|----------|---------------|-------| | 1 | PBS Full both CTs | ~21 GB | Simple but may grow | | 2 | DB dump + upload-dir rsync | DB ~1 GB + uploads | Selective | | 3 | DB only offsite | <1 GB | Photos only in Ceph | | 4 | Uploads on Ceph, DB on Galera | DB offsite <1 GB | Clean separation | ### Key Question Where are the actual photo/video uploads stored? CT115 is stopped — uploads may be on a mount point, external volume, or within the PostgreSQL CT. Must identify before choosing a strategy. ## Bandwidth-Aware Offsite Backup Prioritization When adding services to a bandwidth-limited offsite PBS: ### Tier 1: Small + Critical Infrastructure (add first) - Traefik (~3 GB) — reverse proxy config, all routes - Gitea (~1.2 GB) — code repositories - Authelia, DNS — tiny but critical - Paperless (~16 GB) — documents (medium size, high value) ### Tier 2: Medium + Important - Litellm (~3.4 GB) — API config - Seafile config+DB (~2 GB) — without blocks ### Tier 3: Evaluate Separately - Seafile blocks (~558 GB) — consider S3 backend or native backup - Immich uploads — identify storage location first ### Skip - Frigate (~40 GB) — rewritable video, low value - High-churn data that's easily regenerated ## PBS Offsite Job Configuration ### Adding CTs to Offsite Job ```bash # Edit /etc/pve/jobs.cfg from any PVE node (pmxcfs replicates) # Current: vmid 106 # Target: vmid 106,108,104,99999 sed -i '/offsite-ha-daily/,/^$/{s/vmid 106/vmid 106,108,104,99999/}' /etc/pve/jobs.cfg ``` ### Initial Full Backup (Manual) Must run `vzdump` on the node where each CT actually runs (see `proxmox-ve-administration` → `references/pbs-lxc-setup-2026-07.md` pitfall #17 for the silent-skip issue): ```bash # Determine node for each CT first pvesh get /cluster/resources --type vm --output-format json | python3 -c " import json,sys for v in json.load(sys.stdin): if v.get('vmid') in [108,99999,104]: print(f\"CT{v['vmid']:5d} node={v['node']}\")" # Run on correct nodes ssh root@10.0.20.60 "vzdump 108 --storage noris_offsite --mode snapshot --compress zstd" ssh root@10.0.20.70 "vzdump 99999 --storage noris_offsite --mode snapshot --compress zstd" ssh root@10.0.20.91 "vzdump 104 --storage noris_offsite --mode snapshot --compress zstd" ``` ### Verification ```bash # Check backups arrived at PBS (may need content cache refresh) pvesh get /nodes/proxmox1/storage/noris_offsite/content --output-format json | \ python3 -c " import json,sys,datetime for b in sorted(json.load(sys.stdin), key=lambda x: x.get('ctime',0), reverse=True)[:10]: print(f\"{b['volid']:55s} {b.get('size',0)//1024//1024:>8} MB {datetime.datetime.fromtimestamp(b.get('ctime',0)).strftime('%Y-%m-%d %H:%M')}\")" ``` ## Session Context (2026-07-07) - Offsite job expanded: `vmid 106` → `vmid 106,108,104,99999` - Initial full backups triggered manually on correct nodes - CT111 (Seafile) backup showed 0 MB on noris_s3 — empty/failed backup (previous valid: 558 GB on 2026-07-06) - Frigate excluded from all backups per user decision - Seafile backup strategy discussion: user exploring alternatives to 558 GB full backup, considering S3 storage backend (Pro feature) - Immich: CT115 stopped, upload location unidentified