# 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.