Design a Video Streaming Platform on AWS
A worked system-design example for a Netflix/YouTube-style platform — S3 for storage, MediaConvert for transcoding into multiple resolutions, and CloudFront for adaptive-bitrate global delivery.
Want a visual for this topic?
Generate a diagram tailored to Design a Video Streaming Platform on AWS — the AI picks whichever visual (architecture, flowchart, ER diagram, etc.) best fits this specific AWS concept.
Sign in to generate a visual →🎓 Learning objectives
- •Explain why raw uploaded video needs transcoding into multiple resolutions/bitrates before playback
- •Describe the asynchronous upload-to-ready-for-playback pipeline end to end
- •Explain what adaptive bitrate streaming (HLS/DASH) is and why CloudFront/S3 support it naturally
- •Identify the cost/latency tradeoffs of transcoding into many resolution variants
What is it?
A video streaming platform's AWS system design covers the full pipeline from raw video upload to global adaptive-bitrate playback: S3 stores both the raw uploaded source and the processed output, AWS Elemental MediaConvert asynchronously transcodes the source into multiple resolution/bitrate renditions plus HLS/DASH manifests, and CloudFront serves those static segment files and manifests globally from edge locations, with the video player itself handling adaptive bitrate switching based on the viewer's real-time network conditions.
Why it exists
This pipeline exists because video is uniquely expensive to prepare (transcoding is CPU/GPU-intensive and slow) and uniquely sensitive to network conditions during playback (a single fixed-bitrate file plays poorly on both a fast fiber connection, wasting quality, and a slow mobile connection, causing buffering) — the asynchronous transcode-then-serve-adaptively design is the direct, necessary answer to both of those specific constraints.
Problem it solves
It solves reliably converting arbitrary raw uploaded video into a form that plays smoothly across every viewer's actual device and network conditions worldwide, using an asynchronous, event-driven pipeline that doesn't require custom transcoding infrastructure or a specialized video-serving backend beyond standard object storage and a CDN.
Intuition
The entire pipeline exists to bridge the gap between 'one raw file just uploaded' and 'many pre-computed, network-condition-appropriate variants ready to serve instantly to anyone, anywhere' — and because that transcoding step is genuinely slow and expensive, the whole design has to be asynchronous and event-driven rather than something a user waits on synchronously after upload.
Analogy
Raw uploaded video is like a chef's raw ingredients — not yet something a customer can order. MediaConvert is the kitchen turning that into several finished plate sizes (resolutions), and CloudFront is the network of restaurant branches (edge locations) that keep the most popular dishes pre-made and ready nearby, so no customer waits for food to travel from one central kitchen.
Technical explanation
MediaConvert's HLS output group produces a set of .ts (or fragmented MP4) segment files per rendition along with a master .m3u8 manifest listing every available bitrate variant and a per-rendition .m3u8 playlist listing that variant's individual segments — the player (using HLS.js or a native HLS-capable player) parses the master manifest, continuously measures actual download throughput for recent segments, and selects which rendition's playlist to request segments from next, entirely client-side with no server-side involvement in the bitrate-switching decision itself. MediaConvert's EventBridge integration emits Job State Change events with a status field (COMPLETE, ERROR, etc.) that a consuming Lambda can pattern-match on via an EventBridge rule, avoiding any need to poll the MediaConvert API for job status.
Architecture
Raw uploads land in an S3 'incoming' bucket via client-side pre-signed URLs (keeping large file uploads off the application server's own compute path, mirroring the general S3-integration pattern). An S3 event notification triggers a Lambda that submits a MediaConvert job referencing an output group configured for the desired resolution/bitrate renditions and HLS/DASH manifest generation, writing output to a 'processed' bucket. MediaConvert's own EventBridge integration emits a job-status-change event on completion (or failure), consumed by another Lambda that updates the video's status in the application's database. CloudFront sits in front of the processed bucket (via an S3 origin, often with Origin Access Control restricting direct public S3 access), serving the HLS/DASH manifest and segment files globally from edge locations closest to each viewer.
Workflow
- Client requests a pre-signed S3 upload URL from the application and uploads the raw video directly to an 'incoming' S3 bucket. 2) An S3 event notification (on object creation) triggers a Lambda function that starts a MediaConvert transcoding job, configured with an output group producing multiple HLS/DASH renditions. 3) MediaConvert processes the job asynchronously (can take minutes for larger files) and writes output segments/manifests to a 'processed' output bucket. 4) MediaConvert publishes a job-completion event (via EventBridge), triggering a Lambda that updates the video's status to 'ready to play' in the application's database. 5) Viewers stream the video through a CloudFront distribution in front of the processed bucket, with the video player performing adaptive bitrate selection using the HLS/DASH manifest.
Example
A creator uploads a raw 4K video directly to an S3 'incoming' bucket via a pre-signed URL; an S3 event notification triggers a MediaConvert job producing 240p/480p/720p/1080p HLS renditions and manifest files into an 'processed' bucket; once the job completes (notified via EventBridge), the application marks the video 'ready,' and viewers worldwide stream it via a CloudFront distribution fronting that processed bucket, with each viewer's player automatically selecting the rendition matching their current network speed.
Real-world usage
This S3-plus-MediaConvert-plus-CloudFront pattern (or its equivalent using other CDN/transcoding tooling) is the standard architecture behind most video platforms built on AWS, and is a common system-design interview question specifically because it tests understanding of asynchronous processing pipelines, event-driven architecture, and CDN-based content delivery all in one scenario.
Trade-offs
Producing more resolution/bitrate renditions improves playback quality and device compatibility across a wider range of viewer conditions, at real additional MediaConvert processing cost and S3 storage cost — a genuine tradeoff usually tuned based on actual viewer analytics rather than transcoding 'everything possible' by default. Serving video from a CDN in front of S3 dramatically reduces latency and origin load for popular content, at the cost of a cache-invalidation consideration if a video ever needs to be taken down or replaced after publishing.
Visual explanation
Picture a raw video arriving at a loading dock (the incoming S3 bucket), immediately triggering a factory floor (MediaConvert) that produces several correctly-sized packaged versions (renditions) and a shipping manifest listing them (the HLS/DASH manifest), which then get distributed to warehouses near every customer (CloudFront edge locations) — customers automatically get handed whichever package size matches how fast their delivery truck (network connection) can currently go.
Advantages
- —
MediaConvert handles the genuinely complex work of producing multiple correctly-encoded renditions and standards-compliant HLS/DASH manifests without building custom transcoding infrastructure
- —
S3 + CloudFront serve the resulting static segment files and manifests using the exact same simple, highly-scalable static-content-delivery model as any other static asset — no specialized video-serving infrastructure needed
- —
The event-driven pipeline (S3 event → MediaConvert → EventBridge → status update) requires no polling anywhere — each stage reacts to the previous stage's completion
- —
Adaptive bitrate streaming gives every viewer the best playback quality their actual current network conditions support, automatically, with no player-side manual quality selection required
Disadvantages
- —
Transcoding is genuinely slow (potentially minutes for larger files) and cost-scales with both video length and the number of output renditions produced
- —
The asynchronous, multi-stage pipeline means 'upload to ready-for-playback' has real latency the application needs to communicate clearly to the uploader (e.g., a 'processing' status), rather than the video being instantly available
- —
CloudFront caching a video means updates or takedowns require explicit cache invalidation, which isn't instant across every edge location
- —
Storing multiple full renditions of every video multiplies S3 storage cost compared to storing just the original — a real, ongoing cost that scales with content library size, not just upload volume
Common mistakes
- —
Trying to serve the raw uploaded file directly for playback without any transcoding, producing poor playback experience across varying viewer network conditions and devices
- —
Making the upload-to-playback pipeline synchronous (having the uploader's request wait for transcoding to finish) instead of asynchronous with a status update, given how long transcoding can genuinely take
- —
Producing far more resolution renditions than actual viewer analytics justify, adding processing/storage cost for variants that are rarely if ever requested
- —
Serving video directly from S3 without a CDN in front, missing the biggest available latency and origin-cost improvement for globally-distributed viewers
In the AWS Console
- 1
S3 → Buckets → Create bucket, then Properties → Event notifications
Create the incoming and processed S3 buckets, and configure an event notification on the incoming bucket to trigger a Lambda function.
- 2
MediaConvert → Job templates → Create template
Create a MediaConvert job template with an output group configured for HLS adaptive bitrate renditions.
- 3
CloudFront → Distributions → Create distribution
Create a CloudFront distribution with the processed output bucket as its origin, using Origin Access Control.
🎤 Interview questions
Why can't a video be served for playback immediately after upload, without any processing? (Listen for: a single raw uploaded file is typically one fixed resolution/bitrate/codec, which doesn't adapt to different viewers' network conditions and devices; transcoding produces multiple resolution/bitrate variants (e.g., 240p/480p/720p/1080p) so the player can dynamically switch between them as network conditions change, which requires an asynchronous processing step between upload and 'ready to stream')
What AWS service handles video transcoding, and how does it fit into the upload pipeline? (Listen for: AWS Elemental MediaConvert — typically triggered by an S3 event notification the moment a raw video finishes uploading to an 'incoming' bucket, running as an asynchronous job that outputs multiple resolution/bitrate renditions plus HLS/DASH manifest files to an 'processed'/output S3 bucket, notifying the application (via EventBridge/SNS) when the job completes so the video can be marked 'ready to play')
What is adaptive bitrate streaming (HLS/DASH), and how do S3 and CloudFront support it without extra work? (Listen for: the video is split into short segments (a few seconds each) available at multiple bitrates, with a manifest file listing all available renditions; the player continuously monitors the viewer's actual network throughput and requests whichever bitrate segment fits current conditions, switching seamlessly — since HLS/DASH output is just a set of static segment files and a manifest, S3 (as static storage) and CloudFront (as a CDN) serve them exactly like any other static asset, no special video-serving infrastructure needed)
How would you avoid re-transcoding the same expensive job if a job fails partway or is accidentally triggered twice? (Listen for: use the source video's unique object key/ETag combined with idempotency checks before starting a MediaConvert job — check if output already exists or if a job for that input is already in progress before starting a new one — since MediaConvert jobs are relatively expensive, in both cost and time, to run redundantly)
What's the cost/latency tradeoff of transcoding into many resolution variants (e.g., 6 different renditions) versus just 2-3? (Listen for: more renditions give viewers a smoother experience across a wider range of network conditions and devices, but each additional rendition adds MediaConvert processing cost and S3 storage cost for content that may rarely be requested (e.g., a very low-resolution variant almost nobody on modern connections uses) — a real design decision balancing playback quality/compatibility against processing and storage cost, often informed by actual analytics on viewer bandwidth/device distribution)