Vibe Engines
YouTube
System Design

Design Pastebin

Step 1 / 9

Learn system design by building a text-paste service like Pastebin step by step.

The numbers to beatbase62encoding~7charstrillionsof keys

The whole design, in writing

Learn system design by building a text-paste service like Pastebin step by step. An interactive guide covering unique key generation, separating metadata from blob storage, caching and CDN for read-heavy traffic, TTL expiration, privacy, and async analytics.

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

What is Pastebin?

Paste some text, get a short link, share it; anyone who opens the link sees the text. Dead simple — but at scale it’s a sharp lesson in read-heavy design: pastes are written once and read thousands of times, sometimes from all over the world.

CreatorPOST a pasteReaderGET /aZ9k2
New in this step: Creator, Reader.

Mint a unique short key, store the (potentially large) text in a blob store with only a pointer in the database, and make the read path fly with caching and a CDN. It’s the URL shortener’s cousin — but with real content to store and expire.

What the new pieces do

Creatorclient
Pastes some text — a log, a snippet, an error — and gets back a short URL to share. Writes are rare compared to reads.
Readerclient
Opens a shared paste link. Expects the text to load instantly, possibly thousands of times for one popular paste.

Step 1 · The skeleton

Store text, return a key

Two operations: create (take text, give back a key) and read (take a key, give back text). A paste can be a few bytes or a few megabytes, so where the text lives matters from the start.

API ServerMetadata DBObject Store
New in this step: API Server, Metadata DB, Object Store. · swipe to pan the diagram

A paste can be a few bytes or a few megabytes. Where do you put the text?

  1. Multi-megabyte bodies bloat the DB, slow every query, and waste its indexed storage on data you only fetch by key. Keep big bodies out of the database.

  2. The DB holds tiny queryable rows (key → location, TTL, flags) while the object store holds the bulk. Same split as photos/videos — small fast database, bulk where bulk belongs.

  3. In-memory pastes vanish on restart and can’t scale past one machine’s RAM. Pastes must be durably stored, with memory used only as a cache later.

The API Server writes the text to an Object Store and a small row to the Metadata DB mapping the key to that text’s location. Reads resolve the key in the DB, then fetch the body from the blob store.

What the new pieces do

API Serverbackend
Handles both operations: store a new paste and return its key, or look up a key and return the text. The single front door.
Metadata DBstore
Small rows: the key, where the text lives, expiry time, and privacy flag. Queried on every read to resolve a key to its blob.
Object Storestore
The actual paste bodies. Kept out of the database so large pastes don’t bloat it; the DB holds only a pointer here.

Step 2 · The short link

Mint a unique key

Every paste needs a unique, short, URL-safe code like /aZ9k2. Hashing the content collides on identical pastes and yields long codes; pure random forces a “taken?” check on every write.

API Servercreate + fetchKey Genunique short id
New in this step: Key Gen.

Every paste needs a unique short URL-safe code like /aZ9k2. How do you mint it without collisions?

  1. Hashing collides on identical pastes (two people paste the same snippet → same key, overwriting), and full hashes are long. You need short codes unique regardless of content.

  2. Random codes force a "is it taken?" lookup on every create, and collisions rise as you fill the space. Uniqueness by construction avoids the check entirely.

  3. Counting up then base62-encoding guarantees no collisions without any lookup; ~7 chars covers trillions of pastes. (Or hand out pre-generated unused keys.) Unique by construction.

Use a Key Gen Service: take a counter and encode it in base62, or hand out pre-generated unused keys. Codes are unique by construction, so creating a paste never needs a collision check.

  • base62encoding
  • ~7chars
  • trillionsof keys

What the new pieces do

Key Genservice
Mints a unique short code (base62) for each paste so two pastes never collide — uniqueness by construction, no “is it taken?” check.

Back of the envelope

base62, ~7 chars
62^7 ≈ 3.5 trillion keys
counter ⇒ unique by construction
no collision check on create
short + URL-safe
a–z A–Z 0–9, no escaping

Step 3 · Reads dwarf writes

Cache hot pastes

A paste shared in a popular thread gets read thousands of times. Hitting the metadata DB and blob store for every one of those identical reads is wasteful and slow.

API ServerCacheObject Store
New in this step: Cache. · swipe to pan the diagram

A paste in a popular thread is read thousands of times. How do you avoid hitting the DB + blob store every time?

  1. Replicas add throughput but every hot read still does a full store fetch for identical, unchanging content. When the same paste repeats, serve it from memory instead of re-fetching.

  2. A hit returns the text in <1ms; a miss falls through to the blob store and warms the cache. Pastes are write-once, so there’s no invalidation — caching is pure upside from the second read on.

  3. You can’t control reader behavior, and the goal is to serve many readers fast. The system must make repeat reads cheap server-side via caching, not push work onto users.

Put a Cache (Redis) on the read path keyed by paste id. A hit returns the text in under a millisecond; a miss falls through to the blob store and warms the cache. Because a paste’s text never changes, it’s perfectly cacheable.

  • <1mscache hit
  • 100:1reads : writes
  • noinvalidation

What the new pieces do

Cachecache
Redis holding the most-read pastes. A popular link is served from memory in under a millisecond instead of fetching the blob every time.

Back of the envelope

~100:1 reads : writes
write once, read thousands of times
hit ≈ <1ms RAM
vs a DB lookup + blob fetch
immutable ⇒ no invalidation
caching is pure upside

Step 4 · Readers everywhere

Go global with a CDN

Your cache and store sit in one region, but readers are worldwide. A paste opened from another continent pays a long round-trip on every fetch.

ReaderAPI ServerCacheCDNObject Store
New in this step: CDN. · swipe to pan the diagram

Your cache and store sit in one region but readers are worldwide. How do you cut the cross-continent round-trip?

  1. More nodes in one region don’t help a reader on another continent — they still pay the long round-trip on every fetch. You need content closer to the reader, geographically.

  2. Hand-rolling global replication of all content is expensive and heavy when most pastes are never read in most regions. A CDN already does edge caching on demand.

  3. The first reader in a region pulls through the edge; everyone after is served locally. Immutable pastes cache at the edge with long TTLs — the cheapest scaling for read-heavy payloads.

Front the read path with a CDN. The first reader in a region pulls the paste through the edge; everyone after is served locally. Immutable content plus a long TTL means the edge does the heavy lifting.

What the new pieces do

CDNcache
Caches paste content near readers worldwide. Since a paste’s text is immutable, it can sit at the edge with a long TTL.

Back of the envelope

1st regional read pulls through edge
every reader after is served locally
immutable + long TTL
the edge does the heavy lifting
offloads latency + bandwidth
origin barely touched

Step 5 · Don’t keep it forever

Expiration & TTL

Many pastes are meant to be temporary (“expire in 1 hour / 1 day”), and keeping every paste forever steadily fills storage with dead content nobody will ever read again.

API ServerKey GenCacheCDNMetadata DBObject StoreExpiry Worker
New in this step: Expiry Worker. · swipe to pan the diagram

Many pastes are meant to be temporary ("expire in 1 hour"). How do you stop storage filling with dead content?

  1. An expiry on the record makes cleanup routine: expired keys return 404/410, lazy deletion drops them on access, and a background worker (or native store TTL) sweeps the rest. Lifetime becomes data.

  2. Manual cleanup doesn’t scale and risks deleting the wrong thing under pressure. Expiry should be declared per paste and enforced automatically, not firefought.

  3. Keeping every temporary paste forever fills storage with content nobody will read again, and ignores users who explicitly wanted expiry. TTL is both a feature and a cost control.

Store an optional TTL with each paste. An Expiry Worker (or the store’s native TTL) removes expired pastes from the blob store and metadata, after which the key returns a clean 404/410.

What the new pieces do

Expiry Workerworker
Removes pastes whose TTL has passed from the store and metadata, reclaiming space so expired links return 404.

Step 6 · Who can see it?

Privacy & limits

Not every paste should be public. Some are unlisted (only those with the link) or private (only the author). And without size limits, one user can paste a gigabyte and wreck your storage and bandwidth.

CreatorReaderAPI ServerKey GenCacheCDNMetadata DBObject StoreExpiry Worker
The system as it stands at this step. · swipe to pan the diagram

Some pastes should be unlisted (link-only) or private (author-only). How do you enforce that?

  1. An obscure URL is "security by obscurity" — unlisted is not private. Links leak, get shared and indexed, so true privacy needs a real access check, not just a random key.

  2. Hiding from a listing does nothing if anyone with the key can still GET the content. The read path itself must verify permission, not just the discovery surface.

  3. Store a per-paste flag; public serves freely, unlisted needs the key, private requires the author’s auth — checked on every read. Pair with size caps and rate limits so abuse can’t balloon costs.

Add a privacy flag per paste, enforced at the API on every read, and require the right key/auth for private ones. Cap paste size and rate-limit creation so abuse can’t balloon costs or hammer the service.

Step 7 · Counts & scale

Analytics, async

Owners want view counts, but writing an analytics row on every read would slow the one thing that must stay fast — and billions of small rows pile up in one database over time.

API ServerCacheCDNObject StoreExpiry WorkerAnalytics
New in this step: Analytics. · swipe to pan the diagram

Record views asynchronously (fire an event, aggregate later) so reads never block on counting. Shard the Metadata DB by key and lean on the cache/CDN so the read path stays flat as paste count grows into the billions.

What the new pieces do

Analyticsstore
Tracks how often each paste is viewed, updated asynchronously so counting a view never slows the read itself.

You did it

You just designed Pastebin.

CreatorReaderAPI ServerKey GenCacheCDNMetadata DBObject StoreExpiry WorkerAnalytics
The finished design, end to end. · swipe to pan the diagram

Everything you assembled, in order

  • Create/read over an API, with text in a blob store and a pointer in the DB.
  • Base62 keys from a Key Gen Service — unique by construction.
  • A Redis cache makes the read-heavy, immutable path sub-millisecond.
  • A CDN serves pastes from the edge for global readers.
  • TTL-based expiry (lazy delete + sweep) keeps storage from filling with dead pastes.
  • Per-paste privacy flags, size caps and rate limits for the real world.
  • Async analytics and a sharded metadata DB keep the read path flat at scale.

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. How is Pastebin different from a URL shortener?

    Same key-generation idea (base62, unique by construction) and the same read-heavy caching, but Pastebin stores real content. That adds a blob/object store separate from metadata, size limits, expiration/TTL, and privacy — whereas a shortener only stores a tiny target URL. Pastebin is "shortener + content lifecycle."

  2. Why separate metadata from the blob instead of one row?

    Bodies range from bytes to megabytes; inline they bloat the database, slow scans, and waste its indexed storage on data you only fetch by exact key. Splitting keeps the DB small and fast for lookups it’s good at (key → location, TTL, flags) and puts bulk in cheap object storage built for it.

  3. A paste goes viral — how do you handle the hot read?

    It’s the easy case because pastes are immutable: the CDN edge and Redis cache absorb essentially all of it after the first fetch, with no invalidation to worry about. If a single key strains one cache node, replicate that key across nodes or lean on the CDN’s fan-out — the hot-key playbook, made trivial by immutability.

  4. How do you prevent abuse (spam, illegal content, giant pastes)?

    Layered guardrails: a max paste size, per-IP/account rate limits on creation, and content scanning / abuse reporting with takedown. Private and unlisted flags limit exposure but don’t replace moderation. These boring controls keep a free, public, write-accepting service from being weaponized or bankrupted.

  5. How do you expire pastes precisely at scale?

    Two layers: lazy deletion (on read, if past TTL return 410 and delete) handles anything anyone accesses, and a background sweep (or the store’s native TTL index) reclaims the rest no one reads. You don’t need millisecond precision — "gone shortly after expiry, never served once expired" is the guarantee, which lazy + sweep gives cheaply.

Check yourself — the answers, and why

Eight steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. Paste text lives in a blob store (not the DB) because…

    • It’s more secure
    • Big bodies would bloat the DB; keep tiny pointer rows there
    • Blobs are faster to query

    Split metadata from content — small fast DB rows, bulk in object storage.

  2. Base62 keys from a counter are unique…

    • Most of the time
    • By construction — no collision check needed
    • Only if hashed

    Counting up then base62-encoding never collides; ~7 chars covers trillions.

  3. Caching pastes is pure upside because pastes are…

    • Small
    • Immutable (write-once) — no invalidation
    • Private

    No edits means no cache invalidation; the second read onward is essentially free.

  4. A CDN helps most because the workload is…

    • Write-heavy
    • Read-heavy and immutable, with global readers
    • Compute-heavy

    Edge-caching immutable content with long TTLs moves latency and bandwidth off the origin.

  5. "Unlisted" pastes are…

    • Fully private
    • Not secure — true privacy needs an access check on read
    • Encrypted

    An obscure URL isn’t security; private pastes require real auth enforced at the API.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Create: take text, store it, and return a short URL-safe key.
  • Read: GET /aZ9k2 resolves the key and returns the paste text — the hot path.
  • Expire: optional TTL — pastes can die on schedule and return a clean 404/410.
  • Privacy: public, unlisted (link-only), or private (author-only), enforced on every read.
  • View counts: owners see how often a paste is read, counted without slowing the read.

The qualities that shape everything

Each one names the mechanism that buys it.

The database stays small and fast
A metadata/content split — paste bodies live in an object store, the DB holds only a tiny key → location, TTL, flags row.
No two pastes ever collide
A counter encoded in base62 by a Key Gen Service — unique by construction, no "is it taken?" check on create.
Sub-millisecond reads for hot pastes
A Redis cache keyed by paste id in front of the store; a miss falls through and re-warms, and immutable text means no invalidation.
Fast reads for a global audience
A CDN edge-caches immutable paste content near readers with a long TTL, moving latency and bandwidth off the origin.
Storage doesn’t fill with dead pastes
A per-paste TTL with an Expiry Worker (or the store’s native TTL) — lazy delete on read plus a background sweep.
Analytics never slows a read
Views are recorded asynchronously (fire an event, aggregate later) and the metadata DB is sharded by key.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Text in a blob, pointer in the DB over the paste body inline in the database

Multi-megabyte bodies bloat the DB, slow every query, and waste indexed storage on data only fetched by key. A tiny row plus cheap object storage keeps the database small and fast.

Counter + base62 (KGS) over hashing the paste content

Hashing collides on identical pastes (two people paste the same snippet → same key) and yields long codes. A dealt-out counter is unique by construction — no collision check on the write path.

Cache-aside Redis over read replicas of the blob store

Replicas add throughput but every hot read still does a full store fetch for identical content. A cache serves the second read onward from memory in <1ms, and immutable pastes need no invalidation.

A CDN at the edge over replicating the store to every region yourself

Hand-rolled global replication is expensive and wasteful when most pastes are never read in most regions. A CDN edge-caches immutable content on demand — the cheapest scaling for read-heavy payloads.

An access check on every read over relying on a hard-to-guess URL

An obscure URL is security by obscurity — unlisted is not private. Links leak, get shared and indexed, so private pastes need a real permission check on the read path, not just a random key.

What this teaches

Learn system design by building a text-paste service like Pastebin step by step. An interactive guide covering unique key generation, separating metadata from blob storage, caching and CDN for read-heavy traffic, TTL expiration, privacy, and async analytics.

Key takeaways

  • Create/read over an API, with text in a blob store and a pointer in the DB.
  • Base62 keys from a Key Gen Service — unique by construction.
  • A Redis cache makes the read-heavy, immutable path sub-millisecond.
  • A CDN serves pastes from the edge for global readers.
  • TTL-based expiry (lazy delete + sweep) keeps storage from filling with dead pastes.
  • Per-paste privacy flags, size caps and rate limits for the real world.
  • Async analytics and a sharded metadata DB keep the read path flat at scale.

Concepts covered

  • What is Pastebin?
  • Store text, return a key
  • Mint a unique key
  • Cache hot pastes
  • Go global with a CDN
  • Expiration & TTL
  • Privacy & limits
  • Analytics, async
built to be pasted, not memorized — make the calls, drop the cache, run the gauntlet.
Finished this one? 0 / 65 System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More System Designs