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