Low Level Design: Image Processing Service

What Is an Image Processing Service?

An image processing service accepts raw image uploads, applies a configurable set of transformations (resize, crop, compress, watermark, format conversion), and stores the resulting artifacts for downstream consumption. It sits between upload ingestion and content delivery, ensuring that every image stored in the system meets quality, size, and format requirements before it reaches users or CDN edge nodes.

Data Model


-- Tracks each original upload
CREATE TABLE images (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  owner_id      BIGINT NOT NULL,
  original_key  VARCHAR(512) NOT NULL,   -- S3/GCS object key
  mime_type     VARCHAR(64)  NOT NULL,
  file_size     INT          NOT NULL,
  width         INT,
  height        INT,
  status        ENUM('pending', 'processing', 'done', 'failed') DEFAULT 'pending',
  created_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- One row per output variant requested
CREATE TABLE image_variants (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  image_id      BIGINT NOT NULL REFERENCES images(id),
  profile       VARCHAR(64) NOT NULL,    -- e.g. thumb_200, webp_800, avatar
  output_key    VARCHAR(512),
  width         INT,
  height        INT,
  file_size     INT,
  format        VARCHAR(16),
  status        ENUM('pending', 'done', 'failed') DEFAULT 'pending',
  error_msg     TEXT,
  attempt_count TINYINT DEFAULT 0
);

Core Workflow

The pipeline follows a producer-consumer model built around a durable message queue (e.g., SQS, RabbitMQ, or Kafka):

  1. Upload API receives the raw file, stores it to object storage (S3), writes a row to images, and publishes a job message: {image_id: 123, profiles: [thumb_200, webp_800]}.
  2. Dispatcher Worker consumes the job, reads the transformation profiles from a config store, and fans out one message per variant into a processing queue.
  3. Transform Workers (horizontally scaled) each pull a single variant message, download the original from object storage, apply the transformation using a library such as libvips or ImageMagick, and upload the result back to object storage under a deterministic key.
  4. The worker updates image_variants.status = done and emits a completion event. When all variants for an image_id are done, a listener flips images.status = done.

Failure Handling and Retry Logic

Each variant message carries a delivery_count. Workers catch exceptions and implement the following strategy:

  • Transient errors (network timeout, object storage 503): re-enqueue with exponential backoff — delay = 2^attempt seconds, capped at 5 minutes.
  • Permanent errors (corrupt file, unsupported format): mark image_variants.status = failed, write error_msg, and send the message to a dead-letter queue (DLQ) for manual inspection. Do not retry.
  • Max attempts: after 5 retries the message is moved to DLQ regardless of error type.
  • Workers use a visibility timeout (SQS) or consumer acknowledgment (RabbitMQ) so a crashed worker automatically re-exposes the message to peers after the timeout window.

Scalability Considerations

  • Stateless workers: workers hold no local state between jobs; all state lives in the database and object storage, so any number of replicas can be added behind an autoscaler.
  • Queue depth as scaling signal: emit CloudWatch / Prometheus metrics on queue depth; autoscale worker pods when depth exceeds a threshold (e.g., >500 messages).
  • Transformation profiles in config: store profiles in a key-value store (Redis or a config table) so new output formats can be added without redeploying workers.
  • Object storage pre-signed URLs: workers receive pre-signed download/upload URLs so credentials are never embedded in job messages.
  • CDN invalidation: after a variant is written, enqueue a CDN invalidation so stale cached copies are purged automatically.

Summary

A well-designed image processing service decouples ingestion from transformation. Keeping transform workers stateless, driving work through a durable queue, and persisting per-variant status in a relational table gives you reliable retries, simple observability, and horizontal scalability. This is a common system design question in interviews — be prepared to discuss queue semantics, idempotency (using output key as an idempotency token), and how you would handle bursty upload traffic with autoscaling.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “What are the core components of an image processing service?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “An image processing service typically consists of an upload API gateway, an object storage layer (e.g., S3), an asynchronous job queue, one or more worker nodes that apply transformations (resize, crop, filter, compression), a CDN for serving processed images, and a metadata database that tracks job status and output URLs.”
}
},
{
“@type”: “Question”,
“name”: “How do you handle scalability in a high-throughput image processing system?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Scalability is achieved by decoupling ingestion from processing using a message queue (e.g., Kafka or SQS), auto-scaling worker pools based on queue depth, sharding the metadata store, and using a CDN with aggressive caching so processed images are served at the edge rather than hitting origin servers repeatedly.”
}
},
{
“@type”: “Question”,
“name”: “How would you design the storage strategy for original and processed images?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Store originals in durable, versioned object storage (e.g., S3 with versioning enabled). Processed variants can be generated on-demand and cached in a separate bucket or CDN tier. Use a naming convention or database index to map (original_id, transformation_params) to output URLs, enabling deduplication and cache hits for identical transformations.”
}
},
{
“@type”: “Question”,
“name”: “What failure modes should you plan for in an image processing pipeline?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Key failure modes include worker crashes mid-processing (mitigated by idempotent job design and at-least-once delivery with deduplication), corrupt or malformed uploads (handled via validation at ingestion), storage outages (mitigated by retries with exponential backoff and multi-region replication), and queue backlogs under traffic spikes (addressed by auto-scaling workers and setting appropriate job TTLs).”
}
}
]
}

See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering

See also: Snap Interview Guide

See also: Airbnb Interview Guide 2026: Search Systems, Trust and Safety, and Full-Stack Engineering

Scroll to Top