T-HUB/docs/ops/2026-10-12-cloud-hosting-requirements.md

108 lines
5.2 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.

# Cloud hosting requirements — occupymedia.cc stack
Date: 2026-10-12
Basis: measured on the production home deployment (CT 150 app, CT 152 chat,
CT 320 files) — see the table for real numbers, not estimates.
## What runs where
Everything except object storage and the CF edge is self-hostable on ONE VPS:
- Hub app: api, scheduler, web (Next.js), intake site, Element Web client
- Data: Postgres, Redis, Temporal, chat homeserver (tuwunel/RocksDB),
files server (oCIS S3NG + optional Collabora)
- Edge: cloudflared tunnel, nginx edge, certbot, failover watchdogs
- Media worker (transcoding): can stay OFF the box (distributed worker model —
e.g. the planned Windows/NVENC node joins over tailscale)
Externalized by design (never self-host): **R2** object storage (all media
payloads — files, chat media, video), **Cloudflare** (DNS, ingress, failover),
Telegram/Matrix federation peers.
## Measured footprint (production, today)
| Component | RAM used | Disk used | Notes |
|---|---|---|---|
| App CT (everything above) | 1.0 GB / 16 GB | 147 GB* | *images + build artifacts + snapshots — real data is small |
| Chat CT (tuwunel + RocksDB) | 145 MB / 4 GB | 7.8 GB | grows slowly: new media → R2 |
| Files CT (oCIS + Collabora) | 1.4 GB / 8 GB | 13 GB | metadata only; payloads in R2 |
| R2 (object storage) | — | 385 GB on account** | **occupymedia usage ≈ 0 today; the 385 GB is another project sharing the account |
Real concurrent RAM: **~2.6 GB total**. Media payloads are externalized to R2,
so disk does NOT grow with video/uploads.
## Sizing
| Tier | Specs | What it covers | Ref. price (Hetzner-class) |
|---|---|---|---|
| Minimum | 4 vCPU / 8 GB RAM / 160 GB SSD | full stack at current usage | ~€15/mo |
| Comfortable | 8 vCPU / 16 GB RAM / 240 GB SSD | + live-event spikes, on-box transcoding, Collabora sessions | ~€30/mo |
- **CPU**: 4 vCPU is enough if video transcoding stays distributed (render
workers join over tailscale — the design). On-box transcoding wants 8.
- **RAM**: 8 GB floor (Temporal + oCIS + Postgres headroom); 16 GB makes live
events boring.
- **Disk**: does not grow with media (R2 externalized). 80–160 GB lasts years.
- **Bandwidth**: near-zero egress (R2/Cloudflare deliver media; R2 has zero
egress fees).
- **Object storage (R2)**: pay-per-GB (~$0.015/GB/mo) + small ops fees. Current
occupymedia usage ≈ 0; expect low single-digit $/mo at collective scale.
## Single-box placement
One VPS runs the consolidated stack (all three home CTs on one host):
- Docker compose projects: occ-app, occ-chat, occ-files, occ-edge, occ-element,
occ-intake — the same compose files in `infra/occupymedia/`.
- Ingress: Cloudflare tunnel (remotely managed — config lives in the CF
dashboard, re-point at the new box) + the edge nginx + watchdogs (the ipfs3
relay failover works from any location; from a cloud box it is redundant but
harmless).
- Tailscale optional on a cloud box (its address is already public).
## Migration runbook (home → cloud)
Ordered; each step reversible:
1. **Prereq**: fresh VPS (Ubuntu 24.04), Docker + compose plugin, tailscale
(optional), DNS ready (CF API token).
2. **Data export** (from home, per store):
- Postgres: `pg_dump` (owner format) → transfer.
- Chat: stop tuwunel → tar `occ-chat_conduwuit_data` volume (final snapshot
exists; make a fresh one) → transfer. Keep `tuwunel.toml` (has R2 creds).
- Files: stop oCIS → tar `ocis-config` + `ocis-metadata` volumes → transfer.
(Blobs are in R2 — they do not move.)
- Hub files folders: they are oCIS resources (in R2) — nothing extra.
3. **R2**: unchanged — same account, same buckets, same keys. If the VPS region
differs, no action (R2 is anycast).
4. **Restore on the VPS**: start postgres → restore dump → restore chat/files
volumes from tars → start stacks in order (datastores → api/scheduler/web →
chat → files → edge) → apply any pending migrations with the api CLI.
5. **Ingress cutover**: install cloudflared with the same tunnel token (the
tunnel id moves with the token; ingress config is remote) → connections
register from the new box. Re-point tunnel ingress service URLs from
`192.168.13.x`/`100.x` addresses to the VPS's local container names/ports.
6. **Edge + watchdogs**: copy the edge dir; the ipfs3 relay and ingress
watchdog keep working (the watchdog flips DNS to the relay if the new box's
CF path is ever sick).
7. **Verify** (the standard checklist): api/web/chat/files/element health,
login, collective creation mints rooms, archive E2E (upload → R2 → bot →
files), restore tooling round-trip.
8. **Rollback**: home stack stays intact until step 7 passes; DNS flips back in
one API call.
## Cost picture
| Item | Monthly |
|---|---|
| VPS (comfortable tier) | ~€30 |
| R2 storage (grows with media) | ~$1–5 at collective scale, zero egress |
| Cloudflare free tier | $0 |
| **Total** | **~€30–35/mo all-in** |
## What was measured vs assumed
Measured: RAM/disk per CT (table above), R2 bucket inventory + usage, docker
image sizes, component list. Assumed: audience scale stays collective-sized
(tens–hundreds of users); video delivery rides R2/CF (no self-hosted CDN
egress); transcoding off-box. Re-measure after the first large live event.