Video streaming app scaling
Video streaming app scaling and launch checklist
A video streaming app stays fast by paginating every list, indexing the main questions, and deferring heavy work until a measured trigger fires. At launch, apply 10 rules, watch 9 signals and hold the performance targets below.
Last checked
Rules to apply at launch
- Cdn: Segments are immutable, content-addressed objects with Cache-Control: public, max-age=31536000, immutable; only manifests are short-TTL. The origin bucket sees only cache misses.
- Caching: Authorise once per playback session at manifest issue (signed URL, 5-15 min), never per segment; this keeps segment requests fully cacheable and the app out of the byte path.
- Caching: Logged-out browse and title pages use ISR/edge caching with stale-while-revalidate; personalised rows are fetched client-side from one indexed read.
- Pagination: Every list (browse, search, history, studio) uses keyset/cursor pagination on an indexed (sort_key, id); no OFFSET, page size capped at 50.
- Idempotency: Upload completion, transcode callbacks and watch events are idempotent by natural key (video_id, rendition label, client_event_id); retries never create duplicate renditions or events.
- Fanout: Playback telemetry is batched client-side (15 s) and inserted in one multi-row statement; progress is rolled up by cron, so 1M concurrent viewers never write one row per second to a hot row.
- Rate limits: Rate limit per user and per IP on upload creation, manifest issue, search and events; per-plan caps on upload bytes, storage and transcode minutes, enforced before work is accepted.
- Search: Postgres tsvector + GIN with trigram fallback for typo tolerance; the dedicated search engine is deferred until catalog size or query load crosses the stated trigger.
- Storage: Per-user storage quota and lifecycle rules: delete source after successful transcode when the plan says so, expire failed-upload parts after 24 h.
Performance targets
| What to measure | Target |
|---|---|
| Playback start (manifest issue to first frame) | p95 < 2.5 s on a 5 Mbps connection |
| Rebuffer ratio | < 0.5% of play time |
| Browse page LCP | < 2.5 s; TTFB < 200 ms from edge cache |
| Manifest/entitlement API | p95 < 150 ms, <= 3 queries |
| Transcode throughput | <= 1.5x real-time for the 720p rung on one worker; queue wait p95 < 5 min |
| Watch-event intake | p99 < 100 ms, 1 insert statement per batch |
What to watch
- Metric playback start ms: histogram by device class and rendition; the core QoE number.
- Metric rebuffer ratio: client-reported, aggregated per title and per hour.
- Metric transcode queue depth and age: oldest queued job age drives the alert.
- Metric cdn cache hit ratio: segment hit ratio; a drop means a cache-key or TTL regression.
- Log structured transcode and playback logs: request id, video id, user id hash; no tokens or signed URLs.
- Trace upload -> transcode -> ready trace: one trace id from presign to the ready flag.
- Slo playback availability: 99.9% of manifest requests succeed; error budget reviewed monthly.
- Alert transcode stuck: oldest queued job > 15 min or failure rate > 5% over 30 min.
Recurring work
- transcode_video: runs queue (transcode_jobs). Guard: attempt cap 3, per-user concurrent cap, max duration and resolution caps, minutes metered into usage_events.
- rollup_watch_progress: runs cron 1m. Guard: bounded batch, upsert by (user_id, video_id).
- build_catalog_rows: runs cron 15m. Guard: bounded rows per key, atomic swap.
- sweep_stale_uploads: runs cron hourly. Guard: aborts multipart uploads older than 24 h and deletes orphan objects.
Add when the numbers say so
- Read replicas: Route catalog, search and history reads to a read replica once primary CPU or read latency crosses the trigger; code paths already separate reads from writes so this is configuration.
- Queues: Per-title encoding ladders (content-aware bitrate) and GPU/hardware encode pools once transcode cost per hour of content dominates.
Deferred until it is needed
| Deferred | Adopt it when |
|---|---|
| Dedicated search engine (OpenSearch/Typesense) | catalog > 5M titles or search p95 > 300 ms. |
| DRM license service (Widevine/FairPlay/PlayReady) | first licensed-catalog contract. |
| Live ingest (RTMP/WebRTC) and low-latency HLS | live is a launch requirement. |
| Read replicas for catalog/search | primary CPU > 60% sustained or read p95 > 200 ms. |
| ML recommender / embeddings | > 10k daily active viewers. |
On Forge64
Forge64 matches your description to one of 12 reference architectures, writes the plan and data model into the project, generates the app, and runs a quality gate over the code before you see it. Each app gets its own Postgres database and sign-in with no per-user fee. Hosting on Forge Cloud and Forge Payments are rolling out.
Every metered resource on Forge64 has a hard cap, so a traffic spike stops at your limit instead of becoming a surprise bill. This is a reference design for the category, not a promise that every item is generated today. Built-ins such as email, background jobs, file uploads and search are coming soon, so anything here that needs them is something you add yourself for now.
FAQ
- When should I worry about scale for a video streaming app?
- Not before a measured trigger fires. For example, for "Dedicated search engine (OpenSearch/Typesense)": catalog > 5M titles or search p95 > 300 ms.
- What performance targets should a video streaming app aim for?
- Playback start (manifest issue to first frame): p95 < 2.5 s on a 5 Mbps connection. Rebuffer ratio: < 0.5% of play time. Browse page LCP: < 2.5 s; TTFB < 200 ms from edge cache.
- Does Forge64 cap my usage costs?
- Yes. Every metered resource has a hard cap, and paid plans add an overage spend cap that you set.
Build your app with Forge64
One prompt to a running app with auth, database, hosting and payments. Start free, no credit card required.
Start building for free