Design Pastebin

EasyCore Building BlocksObject StorageCachingCDNExpiration

Problem

Design a service where a user pastes a block of text, such as a code snippet or a log excerpt, and receives a short URL like paste.io/k8Fq2Lm that anyone can open to read it. Pastes can be public or unlisted, and the author can set an expiration time (for example 10 minutes, 1 day, or never). A paste can be up to 10 MB, though most are a few kilobytes. The service should handle 5 million new pastes per day and roughly ten times as many reads.

  • Do users need accounts, or are anonymous pastes allowed?
  • Can a paste be edited after it is created, or are pastes immutable?
  • Do we need syntax highlighting, and is it rendered on the server or in the browser?
  • Should "burn after reading" pastes be supported, where a paste is deleted after the first view?
  • Do we need full-text search across public pastes?
  • How should we handle abuse such as leaked credentials or malware payloads?

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • Create a paste from text and return a unique short URL.
  • Read a paste by its URL.
  • Support public and unlisted visibility.
  • Expire pastes at an author-chosen time.
  • Let a signed-in author list and delete their own pastes.
  • Serve a raw text view for use with command-line tools like curl.

Items in bold are existing requirements. The rest are suggestions. Confirm with the interviewer whether they are relevant first.

  • Handle 5 million new pastes per day (about 60 writes per second, peaking near 300).
  • Reads average about 600 per second and peak near 3,000, with a long tail of popular pastes shared on social media.
  • Accept pastes up to 10 MB, with an average size of about 10 KB.
  • Storage grows by roughly 50 GB per day, or about 18 TB per year before expirations are removed.
  • Read latency under 100 ms at p99 for cached pastes and under 300 ms for cold ones.
  • High availability for reads (99.9%) and durability for stored content (no silent loss of a paste before it expires).
  • Paste keys must be unguessable so unlisted pastes cannot be enumerated.

A stateless paste service stores metadata in a key-value store and paste bodies in object storage, keyed by a random short code. Reads go through a CDN and an in-memory cache, since paste content is immutable and caches well. A background sweeper deletes expired pastes from both stores, while reads also check the expiration so an expired paste is never served even before the sweeper reaches it.

Loading diagram...

Create a paste. The client sends POST /pastes with the text, visibility, and expiration. The paste service generates a random 8 character base62 key, uploads the body to object storage under that key (compressed with gzip), and then writes the metadata row with a conditional insert so a rare key collision is detected and retried. It returns the short URL.

Read a paste. The browser requests GET /k8Fq2Lm. The CDN serves the response if it has a fresh copy. Otherwise the request reaches the paste service, which loads metadata from the cache or the metadata store, rejects the request with 404 if the paste is missing or past expires_at, and then fetches the body from the cache or object storage. The response carries a Cache-Control max age no longer than the remaining lifetime of the paste.

Expire a paste. The sweeper scans the metadata store's expiration index every minute, deletes the object and the metadata row for each expired paste, and issues a CDN purge for its URL.

Stateless HTTP service that creates and serves pastes. It keeps no local state other than connection pools, so any instance can handle any request.

  • Node.js or Go service behind an L7 load balancer, autoscaled on CPU and request rate.
  • Streams large bodies to object storage instead of buffering them fully in memory.

API

  • POST /pastes with { content, visibility, expiresIn?, syntax? } returns { key, url }.
  • GET /:key returns the rendered HTML page, or 404 if the paste is missing or expired.
  • GET /raw/:key returns text/plain content.
  • DELETE /pastes/:key deletes a paste (author only).
  • GET /users/me/pastes?cursor= lists the author's pastes.

Scalability

Peak traffic of a few thousand requests per second needs only a small fleet, and most reads never reach it because the CDN answers them. Bodies over 1 MB are uploaded with a presigned URL directly from the client to object storage, so large uploads do not tie up service instances.

Tradeoffs

Rendering syntax highlighting in the browser keeps the server fast and lets the stored body stay plain text. Server-side rendering would help search engine indexing of public pastes, but the added CPU per request is not worth it for a service whose pages are mostly unlisted.

Durable record of each paste: who created it, when it expires, and where its body lives.

  • DynamoDB or Cassandra, both of which offer conditional writes and native TTL.

Data Model

  • pastes: key (partition key), author_id, visibility, size_bytes, syntax, created_at, expires_at, object_key.
  • pastes_by_author: author_id (partition key), created_at (sort key), key.
  • expiry_index: expiry_bucket (partition key, one per minute), key, used by the sweeper.

Scalability

At about 200 bytes per row, even a billion pastes is only around 200 GB of metadata. Random keys spread writes evenly across partitions. The expiry index is bucketed by minute so the sweeper reads one small partition at a time instead of scanning the table.

Tradeoffs

Keeping bodies out of the metadata store keeps rows small and cheap to replicate, at the cost of a second lookup per cold read. A relational database would also work at this size, but every query is a point lookup by key, and native TTL plus easy horizontal scaling make a key-value store the simpler long-term choice.

Holds the compressed text of every paste.

  • Amazon S3, Google Cloud Storage, or another S3-compatible store.

Data Model

  • Object key: pastes/<key>, with Content-Encoding: gzip and the paste key as metadata for orphan cleanup.

Scalability

Object storage scales storage and request rate without operational work and replicates across zones for durability. Text compresses well, typically 3 to 5 times with gzip, which cuts the 18 TB per year estimate substantially. Lifecycle rules move pastes older than 90 days to an infrequent access tier.

Tradeoffs

Storing bodies in the database would remove a network hop, but 10 MB rows are a poor fit for most databases and inflate replication and backup costs. Object storage is far cheaper per gigabyte. Because the body is written before the metadata row, a crash between the two leaves an orphaned object rather than a paste that points at nothing, and a weekly job deletes objects with no metadata.

Two layers of caching that absorb the read traffic for popular pastes.

  • A CDN such as CloudFront or Fastly in front of the service.
  • Redis or Memcached for metadata and small bodies (under 100 KB) inside the data center.

Scalability

Pastes are immutable, so cached copies never go stale except through deletion or expiration. A viral paste is served almost entirely from CDN edges. The in-memory cache uses LRU eviction and only stores small bodies so a few large pastes cannot evict thousands of small ones.

Tradeoffs

The CDN cannot cache unlisted pastes any differently from public ones, which is acceptable because unlisted security relies on the unguessable key, not on hiding the response. Deleting a paste requires a CDN purge, which can take seconds to propagate. That short delay is accepted in exchange for offloading most reads.

Background worker that removes expired pastes from every store.

  • A scheduled job running every minute, with a distributed lock so only one instance works on a bucket at a time.

Scalability

Each run reads one expiry bucket, so its work is proportional to the number of pastes expiring that minute rather than to the total library. If the sweeper falls behind, nothing incorrect is served, because the read path checks expires_at itself.

Tradeoffs

Relying only on the database's native TTL would delete metadata but leave bodies in object storage and stale copies on the CDN. An explicit sweeper costs a small amount of code but cleans up all three in one place.

Related questions