Skip to content

Pastebin#

Problem statement (interviewer prompt)

Design a Pastebin: anyone can paste text, get a short URL, and others can read it. Support expiry, password-protect, syntax highlighting, edit/delete by owner, and view counters. Optimise for read-heavy (10:1) and 1M new pastes/day.

flowchart LR
  U([User])
  W([Web / API])
  ID([ID Generator])
  OBJ[(Object Storage<br/>paste body)]
  META[(Metadata DB<br/>id, owner, expiry)]
  CDN[CDN]
  U -->|POST /paste| W
  W --> ID
  W --> OBJ
  W --> META
  U -->|GET /:id| CDN --> W
  W --> META
  W --> OBJ

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class U,W client;
    class CDN edge;
    class META datastore;
    class ID compute;
    class OBJ storage;
flowchart TB
  subgraph Client
    BR([Browser])
    CLI[CLI / API]
  end

  subgraph Edge
    DNS[DNS]
    CDN[CDN / Edge cache]
    LB[L7 LB]
    WAF[WAF + abuse]
  end

  subgraph App[App Tier]
    GW[API Gateway]
    CRT[Create Service]
    READ[Read Service]
    DEL[Delete / Expire]
    SYN[Syntax highlighter<br/>server-side]
    AUTH[Auth - opt]
  end

  subgraph ID[ID generation]
    SF[Snowflake / KGS]
    B62[Base62 encode<br/>8 chars]
  end

  subgraph Storage
    META[(Metadata SQL<br/>paste id, owner, expiry, lang)]
    BLOB[(Object store S3 / GCS<br/>raw paste body)]
    CACHE[(Redis hot pastes)]
  end

  subgraph Async
    Q[[Kafka events<br/>create / view / delete]]
    SC([Expiry sweeper<br/>cron])
    ANL[Analytics<br/>views, languages]
    AB([Abuse scanner<br/>secret detection])
  end

  subgraph Obs
    M[Metrics] 
    L[Logs] 
    T[Traces]
  end

  BR --> DNS --> CDN --> LB --> WAF --> GW
  CLI --> DNS
  GW --> CRT
  GW --> READ
  GW --> DEL
  CRT --> SF --> B62
  CRT --> META
  CRT --> BLOB
  READ --> CACHE
  CACHE -. miss .-> BLOB
  READ --> META
  READ --> SYN
  DEL --> META
  DEL --> BLOB
  SC -.expire.-> META
  SC -.delete.-> BLOB
  CRT --> Q
  Q --> ANL
  Q --> AB
  AB -.flag.-> META
  App -.metrics.-> M

    classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
    classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
    classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
    classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
    classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
    classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
    classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
    classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
    classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
    classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
    class BR client;
    class DNS,CDN,LB,WAF,GW edge;
    class CLI,CRT,READ,DEL,SYN,AUTH,B62,ANL service;
    class SF,META datastore;
    class CACHE cache;
    class Q queue;
    class SC,AB compute;
    class BLOB storage;
    class M,L,T obs;

Storage choice#

  • Body in object store (S3) - keyed by paste id; cheap, durable.
  • Metadata in SQL (or DynamoDB) for indexing by owner, recent, expiry.
  • Hot pastes cached in Redis as id → body (TTL 1 hr).

Expiry#

  • Either: (a) explicit cron sweeping expires_at < now, (b) S3 lifecycle policy delete.
  • Soft-delete then GC for undo window.

Privacy modes#

  • Public / Unlisted / Private (password-protected).
  • For private: store paste encrypted with key derived from URL fragment (zero-knowledge - server can't read).

Anti-abuse#

  • Scan content for API keys, PII; rate-limit per IP.
  • Captcha + honeypot for anonymous create.

Glossary & fundamentals#

Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.

Tag Concept What it is Page
HLD Load balancer / GSLB L4/L7 traffic distribution and failover load-balancer
HLD CDN edge caching for static assets cdn
HLD API gateway / BFF single ingress, auth, rate limit, routing api-gateway
HLD Pub/Sub & message brokers topics, consumer groups, delivery semantics pub-sub-pattern
HLD Observability metrics, logs, traces, SLOs observability
HLD Search internals inverted index, BM25, embeddings, ANN search-internals

Quick reference#

Functional#

  • Anonymous or authenticated paste creation.
  • Short URL → text body retrieval.
  • Optional: expiry, password, language tag, view counter.
  • Edit / delete by owner.

Non-functional#

  • Read-heavy (10:1).
  • p99 read < 200 ms.
  • Durable: never lose body once written.

Capacity#

  • 1M new pastes/day → 12/s avg.
  • 10M reads/day → 120/s avg.
  • Avg paste 10 KB → 10 GB/day → ~3.6 TB/year.
  • Hot working set 1 day: ~10 GB cache.

API#

POST /v1/paste {text, lang, expiry, password?} -> {id, url}
GET  /v1/paste/{id}                            -> {text, lang, ...}
DELETE /v1/paste/{id}                          (owner / token)

Schema#

pastes(id PK, owner_id, lang, expires_at, created_at, size, sha256, abuse_flag)
Body lives in S3 at s3://bucket/p/<id>.

Trade-offs#

  • Store body in DB (simple) vs S3 (cheap at scale). S3 wins beyond a few GB.
  • Single-region simple; multi-region needs S3 replication + DB replication.
  • Strong vs eventual read after write: read-after-write critical for newly created pastes - pin first read to same DC.
  • Encryption client-side prevents server abuse but no server-side search.

Refs#

  • pastebin.com architecture posts, GitHub Gist design, ByteByteGo "Design Pastebin" video, Alex Xu Vol 1.

FAQ#

How do you generate paste URLs?#

Generate a base62 key from a random source or counter, check for collision in the metadata DB, and retry on conflict. Six to eight chars give ample address space.

Where do you store the paste body?#

Object storage like S3 keeps unbounded text cheap, while a relational or KV metadata DB holds id, owner, expiry, and password hash. CDN caches the body for read scale.

How does TTL expiry work for pastes?#

Store an expiresAt timestamp on the metadata row and let either lazy deletion on read or a daily janitor reclaim object storage and remove expired rows.

How do you scale a read-heavy paste service?#

Front the GET endpoint with a CDN, cache hot pastes in Redis, shard the metadata DB by paste ID, and serve the body straight from object storage where possible.

How are password-protected pastes implemented?#

Store a bcrypt hash on the metadata row. On read the user submits the password, the API verifies the hash, and only then returns the body from object storage.

  • Object Storage: blob and object stores back the raw paste content at scale
  • Caching Strategies: read-aside and CDN caching reduce latency for popular pastes
  • CDN: edge delivery for static paste content worldwide

Video walkthrough

Design PasteBin (Systems Design Questions 3.0) : via Jordan has no life (Ex-Google SWE)