Files
obsidian-vault/200 projects/220 Web Host Migration/Backup Architecture — Offsite to AWS S3.md

97 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
created: 2026-08-28
modified: 2026-08-28
type: note
client: sam
project: web-host-migration
status: active
priority: 1
tags:
- backup
- aws
- s3
- glacier
- obsidian
aliases:
- backup-architecture
- offsite-backup
id: 1848129024-BAC
---
# Backup Architecture — Offsite to AWS S3 (Disaster / "House burns down")
> Part of [[Migration Plan — InMotion to Local + AWS Offsite]], Phase 1.
> Status: **bucket + lifecycle + pipeline built & validated. Seeding pending user go/no-go.**
> Source-set refined from the Google-photos migration note (see "Source set" below).
## Why
Protect the "house burns down" set off-machine so a total local loss doesn't destroy photos, site data, or Borg backups. Local copies alone (Borg/rsync/Kopia between .13/.35/.23) are all on-site — a fire takes them all. AWS is the off-site copy.
## Tech stack (what we actually use)
- **AWS S3 bucket** `sam-offsite-backup` — region **ap-southeast-2 (Sydney)**
- Block **all public access**, **Versioning Enabled**, **SSE-S3** server-side encryption at rest
- **Lifecycle rule** `archive-to-glacier`: S3 Standard (first 90 days, fast restore) → **Glacier Flexible** @ 90d → **Glacier Deep Archive** @ 180d (cold/cheap)
- **IAM user** `backup-offsite` — programmatic, **least-privilege**, only object-level S3 on this one bucket (ListBucket/PutObject/GetObject/DeleteObject). No bucket-management, no console, no other AWS services. Root keeps full power.
- **Tool:** **AWS CLI** `aws s3 sync` (chosen over rclone — rclone fought S3 region discovery with the scoped IAM; AWS CLI works with object-only perms). Validated: full upload/list/readback/delete test succeeded.
- **Local driver:** pending — will be a NixOS systemd timer (won't be in `backup.nix` to avoid disrupting existing Borg; separate service) running from `.13`, reading creds from `/home/sam/.aws-credentials.local` (0600).
## Source set (what we back up)
From **.13** (unless noted):
| Source | Path | Size |
|---|---|---|
| Photo master (by_date) | `/mnt/ubuntu_storage_3TB/archive/03_photos/by_date` | 373 GB |
| Immich phone-only | `.35:/mnt/hd/immich/library` (via SSH to .35) | small |
| Projects / notes / docs | `/mnt/ubuntu_storage_3TB/archive/` (`01_family…08_transfer`, minus `03_photos`) | ~177 GB |
| `.27` Borg repo | `/mnt/ubuntu_storage_3TB/backup/borg/.27` | 162 GB |
| **Total** | | **~712 GB** |
### Explicitly SKIPPED (duplicates/transient/regenerable)
- `by_subject` (69 GB) — duplicate of `by_date` (different folder order)
- `/mnt/data/takeout/` (269 GB) — transient Google-photos download, deleted after verification
- Immich thumbs / encoded-video — regenerable
- 14 zip archives — redundant
- Immich library copies of `by_date` photos — duplicates
## Proposed bucket layout (prefixes)
```
s3://sam-offsite-backup/
├── photos/by_date/ (373G)
├── photos/phone-only/ (small, from .35 Immich)
├── archive/rest/ (177G)
└── borg/.27/ (162G)
```
## Procedure to run a seed / sync
On `.13` (after tooling installed):
```bash
source /home/sam/.aws-offsite-env.sh # sets AWS_* from creds file
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
aws s3 sync /mnt/ubuntu_storage_3TB/archive/03_photos/by_date \
s3://sam-offsite-backup/photos/by_date/ --storage-class STANDARD
```
- `s3 sync` is **incremental** — after the initial seed, re-runs upload only new/changed files.
- Bandwidth: **~3.3 MB/s** measured → ~62 h for full 712 GB seed.
## Cost (AUD, honest — revised 2026-08-28)
Seeding everything in **S3 Standard** (fast restore) means the first **90 days** cost the most:
- **Months 0–3 (all S3 Standard):** ~712 GB × $0.036 ≈ **~$26/mo**
- **Months 3–6 (Glacier Flexible):** ~$0.005/GB ≈ **~$3.6/mo**
- **After month 6 (Deep Archive):** ~$0.0015/GB ≈ **~$1.1/mo**
- One-time upload bytes: **free** (AWS ingress is free) ✓
### Cheaper option: send cold data straight to Glacier Deep Archive
If photos + archive are genuinely cold (only needed in disaster), upload them with `--storage-class DEEP_ARCHIVE` directly → **no 90-day Standard wait**, ~**$1/mo immediately** for the cold set. Only *recent/active* data (Borg .27 repo + phone-only) stays in Standard. Downside: Deep Archive restore takes **hours** (acceptable for disaster recovery). This could reduce peak to **~$5–8/mo** total.
> The two choices are a small cost-vs-restore-speed tradeoff; **both are well under the 20–50 AUD/mo budget** and ~75%+ cheaper than the retired $100 VPS.
## Pending / Open
- [ ] User confirms go/no-go on seed scope + storage class (Standard vs Deep Archive for cold set)
- [ ] Install AWS CLI declaratively on `.13` + systemd timer (managed NixOS service) — but **not** in existing `backup.nix` (protect current Borg jobs); separate config
- [ ] Begin first seed (run overnight / across days given ~62 h)
- [ ] **Verify a test restore** from S3 after seed (critical)
- [ ] Decide optional "delete noncurrent versions" lifecycle to cap version bloat
- [ ] Security: rotate access key after seeding (it was shared in chat); root MFA now enabled
---
*See [[Migration Work Log]] for live track record; [[Migration Plan — InMotion to Local + AWS Offsite]] §9 for the master cost model.
*➡ **Restore procedures: [[Restore Runbook]]** (how to bring back each machine, local or S3).*