Design Pastebin: How to Build a Production-Grade Paste Service That Won't Fall Over at 3 AM
Design pastebin for production: learn how to handle text storage, expiry, rate limiting, and sharding with real-world trade-offs and war stories..
20+ years shipping large-scale distributed systems. Lessons pulled from things that broke in production.
- ✓Solid grasp of fundamentals
- ✓Comfortable reading code examples
- ✓Basic production concepts
To design a pastebin, you need a web server, a database (SQL or NoSQL), a unique ID generator (like base62 encoding of a counter or UUID), and a background job for expiry. Key trade-offs: SQL for consistency vs NoSQL for scale, and client-side vs server-side deduplication.
Think of a pastebin like a public bulletin board where you can pin a note and get a ticket stub with a number. Anyone with the stub can read the note. The board automatically tears down old notes after a while. If someone tries to pin the exact same note twice, the board just hands them the same stub instead of wasting space.
Most pastebin tutorials are toy projects that die the second they see real traffic. They use a single database, no caching, and no rate limiting. I've seen a paste service take down an entire API gateway because one user uploaded a 50MB log file and the server tried to load it all into memory. Don't be that team. Here's how to build a pastebin that survives production.
The core challenge is simple: accept text, store it, give back a short URL, and delete it after a TTL. But the devil is in the details — how do you generate unique IDs at scale? How do you handle duplicate pastes? What happens when a paste is 100MB? How do you prevent abuse? This article answers all of that with battle-tested patterns.
By the end, you'll be able to design a pastebin that handles 10K writes/sec and 100K reads/sec, with proper expiry, deduplication, and rate limiting. You'll also know exactly when to use SQL vs NoSQL, and why your first instinct (just hash the content!) might burn you.
Why Most Pastebin Designs Fail at Scale
The textbook pastebin design uses a single SQL database, generates IDs via auto-increment, and stores pastes as TEXT columns. This works for 100 users. At 10K users, the auto-increment becomes a bottleneck (every insert locks the sequence). At 100K users, the TEXT column causes table bloat and slow queries. And if you ever need to shard, auto-increment IDs become a nightmare. The fix: use a distributed ID generator like Snowflake or a key-value store with content-addressed hashing. Also, separate metadata (short URL, user, expiry) from content (the actual paste text) — store content in blob storage like S3, and metadata in a fast database like Cassandra or DynamoDB.
Deduplication: Why Hashing Alone Isn't Enough
Deduplication saves storage: if two users paste the same content, store it once and return the same URL. The naive approach: hash the content (SHA256) and use the hash as the storage key. Problem: hash collisions are astronomically unlikely, but content changes (e.g., trailing newline) produce different hashes. So you need to normalize content (trim whitespace, unify line endings) before hashing. Even then, you might want to allow duplicates for different users (e.g., for analytics). The better approach: store content by hash, but return a unique short URL per paste. The hash is just for storage dedup. Metadata still has a unique short URL per paste. This way, you save storage but preserve per-paste identity.
Expiry: How to Actually Delete Pastes Without Breaking Reads
Expiry is easy to get wrong. The simplest approach: set a TTL in the database and run a cron job to delete expired rows. But if the cron job fails, expired pastes linger. Better: use database-level TTL if supported (DynamoDB TTL, Redis EXPIRE, Cassandra TTL). For S3, use lifecycle policies. But there's a catch: if you delete the content from S3 before all metadata references are cleaned up, reads will 404. Solution: soft-delete metadata first, then delete content after a grace period. Or, use a reference count: only delete content when no metadata references it. For simplicity, set S3 lifecycle to delete objects after 30 days, and delete metadata after 7 days. The content will be cleaned up eventually.
Rate Limiting: How to Stop Abuse Without Hurting Legit Users
Pastebin is a prime target for spam and abuse. Without rate limiting, a single user can upload thousands of pastes per second and fill your storage. The standard approach: token bucket or sliding window per user (IP or API key). But IP-based limiting is fragile behind NAT. Better: use API keys for authenticated users, and a CAPTCHA for anonymous uploads. For the rate limit itself, use a Redis-backed sliding window counter. Set limits: 10 pastes per minute for anonymous, 100 per minute for authenticated. Return 429 Too Many Requests with a Retry-After header. Also, implement a global rate limit to protect the database from traffic spikes.
Reading Pastes: Caching Strategies That Actually Work
Pastebin is read-heavy: a popular paste can get millions of views. Without caching, every read hits the database and S3, causing high latency and cost. The solution: cache metadata in Redis (or Memcached) and cache content in a CDN (CloudFront, Cloudflare). For metadata, cache the short URL → content URL mapping with a TTL of a few minutes. For content, set S3 bucket as an origin for CDN and cache with a long TTL (e.g., 24 hours). But beware: if a paste is deleted, the CDN might serve stale content. Use cache invalidation or short TTLs for sensitive data. Also, implement a read-through cache: on cache miss, fetch from DB and populate cache.
Sharding: When One Database Isn't Enough
At massive scale (billions of pastes), a single database can't handle the write throughput. You need to shard. The simplest sharding key is the short URL's first character (or a hash of it). But that leads to hot spots (e.g., 'a' might have more pastes than 'z'). Better: use consistent hashing on the short URL. Distribute shards across multiple database instances. For reads, you need to know which shard to query: either embed the shard ID in the short URL (e.g., first 2 chars = shard ID) or use a lookup service. The former is simpler: generate short URLs with a prefix that maps to a shard. For example, shard 0 handles URLs starting with '0'-'9', shard 1 handles 'a'-'z', etc. But this requires rebalancing when adding shards. Consistent hashing minimizes rebalancing.
When Not to Use This Design
This design is overkill for a small internal pastebin with <100 users. In that case, just use a SQLite file and a simple HTTP server. Also, if you need strong consistency (e.g., paste must be immediately readable after upload), avoid eventual consistency caches and CDNs. For compliance (e.g., GDPR right to deletion), you need immediate cache invalidation, which adds complexity. Finally, if your pastes are tiny (<1KB) and you have few users, just store them in the database directly — no need for S3. The trade-off is simplicity vs scalability.
The 4GB Container That Kept Dying
- Never buffer the entire request body in memory.
- Stream or die.
`curl -v -X POST -d @largefile.txt https://pastebin.example.com/api/paste``kubectl top pod -l app=pastebin`client_max_body_size 10m; in nginx and add streaming upload.| File | Command / Code | Purpose |
|---|---|---|
| PasteStorage.systemdesign | CREATE TABLE paste_metadata ( | Why Most Pastebin Designs Fail at Scale |
| Deduplication.systemdesign | function normalizeContent(content: string): string { | Deduplication |
| Expiry.systemdesign | DELETE FROM paste_metadata WHERE expires_at < NOW(); | Expiry |
| RateLimiter.systemdesign | const redis = require('redis'); | Rate Limiting |
| ReadCache.systemdesign | async function getPasteMetadata(shortUrl: string): Promise | Reading Pastes |
| Sharding.systemdesign | const hashRing = new ConsistentHashRing(); | Sharding |
Key takeaways
Interview Questions on This Topic
How does your pastebin handle duplicate content under concurrent uploads? What if two users upload the same paste at the exact same time?
Frequently Asked Questions
20+ years shipping large-scale distributed systems. Lessons pulled from things that broke in production.
That's Real World. Mark it forged?
3 min read · try the examples if you haven't