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#
Body lives in S3 ats3://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.
Related Topics#
- 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