Guide · checked 31 October 2026

Serve and resize images from your own S3 or R2 bucket

Keep the originals in a bucket you own and let an image server resize them on the way out. Here are the services that read from your bucket, how each one connects, and what it costs.

Written by the Flatpix team.

We build Flatpix, one of the five options below, so weigh its section as the vendor's own. Every claim about another product links to that product's documentation in the sources.

The short answer

An image CDN does not need your files in its own media library. Several services read the originals straight from your Amazon S3, Cloudflare R2 or Google Cloud Storage bucket, resize and convert them on request, and cache the result. Your bucket stays the single copy of the truth.

  • Hosted, many storage types: imgix and ImageKit both connect to S3, R2, Google Cloud Storage, Azure and other S3-compatible stores.
  • Already on Cloudflare: Cloudflare's image transformations work on images in your zone, and on other origins you allow.
  • Run it yourself: imgproxy and Flatpix both read s3:// buckets. Flatpix also reads Cloudinary-style URLs, which matters if you are moving off Cloudinary.

Why keep originals in your own bucket

  • You own the files. If you change image service, the originals do not move. You point the next service at the same bucket.
  • Storage at bucket prices. Media libraries often bundle storage into credits or plan tiers. A bucket bills storage on its own. On Cloudflare R2 that is $0.015 per GB-month with no fee for data sent to the internet.
  • One upload path. Your app already writes to the bucket. Nothing has to be pushed to a second system or kept in sync.
  • Backups and lifecycle rules you control. Versioning, replication and retention are bucket settings, not a vendor feature.

The trade-off: you give the image service read access to the bucket, and the first request for each size reads the original from it. A cache in front, or a derived cache in the bucket itself, keeps those reads rare.

The five options

ServiceBuckets it readsRuns whereURL style
imgixAmazon S3, Google Cloud Storage, Azure, DigitalOcean, Cloudflare R2, Wasabi, Linode, other S3-compatible, or a web folderHosted?w=400&auto=format
ImageKitAmazon S3, S3-compatible storage, Google Cloud Storage, Azure, DigitalOcean, Wasabi, a Cloudinary backup bucket, or a web serverHosted?tr=w-400, or Cloudinary style with its rewriter
Cloudflare image transformationsImages in your Cloudflare zone, including an R2 bucket on your domain, plus origins you allowHosted, on Cloudflare/cdn-cgi/image/width=400/
imgproxyAmazon S3 and S3-compatible storage through s3:// source URLs, plus othersYour serversIts own signed path format
Flatpix (ours)Amazon S3, Cloudflare R2, MinIO, Google Cloud Storage in interoperability mode, or a folderYour servers, or hostedCloudinary style: /image/upload/w_400/

Sources: each service's documentation, linked in the sections and in the sources, 31 October 2026.

Each one, briefly

imgix

imgix calls a connected bucket a Source. You create one per storage location, give imgix read credentials, and get an imgix.net subdomain or use your own. The list of supported storage is the longest of the five. It charges in credits; its pricing is covered in our alternatives guide.

Source: imgix: Creating sources, 31 October 2026.

ImageKit

ImageKit connects external storage as an origin, next to its own media library. It is the only hosted option here that can read a Cloudinary backup bucket as an origin, which helps when you are leaving Cloudinary. Its URL Rewriter keeps Cloudinary transformation strings working.

Sources: ImageKit: Connect external storage and Cloudinary backup bucket, 31 October 2026.

Cloudflare image transformations

If your bucket is R2 and your site is on Cloudflare, transformations run where the files already are. By default Cloudflare only transforms source images from the zone that serves the transformation, so you either serve the R2 bucket on a domain in that zone or add the bucket's host to the allowed origins.

Source: Cloudflare Images: sources, 31 October 2026.

imgproxy

imgproxy is an open-source image server you run yourself. With S3 support switched on, a source URL of the form s3://bucket/key reads the object directly, and an endpoint setting points it at S3-compatible stores.

Source: imgproxy: Amazon S3, 31 October 2026.

Flatpix (ours)

Flatpix is an open-source image server in Go, built on libvips, that reads Cloudinary URLs. Its origin is a folder or an s3:// bucket, and its derived cache can live in the same bucket. Each cloud name in the URL can map to its own bucket, so one server can serve several brands or stores. The test suite runs the S3 origin and cache against an in-memory S3 server; if you run it on R2, MinIO or Google Cloud Storage, tell us what you find on GitHub. Hosted Flatpix is a flat $39, $99 or $249 a month with your originals still in your bucket.

Source: Flatpix README: origins, 31 October 2026.

Setting it up with an S3 or R2 bucket

The steps are the same for any of the five: make credentials that can only read, point the service at the bucket, and check one URL.

  1. Make a read-only key. On S3, an IAM user or role with s3:GetObject on the bucket (and s3:PutObject on a cache prefix if the service writes its cache there). On R2, an API token scoped to the bucket.
  2. Pick the layout. Keep originals under one prefix, such as originals/, and the image server's cache under another, such as derived/. A lifecycle rule can clear the cache prefix without touching the originals.
  3. Connect the service. For Flatpix, six settings:
    docker run -p 8080:8080 \
      -e FLATPIX_ORIGIN=s3://my-media/originals \
      -e FLATPIX_S3_ENDPOINT=https://ACCOUNT.r2.cloudflarestorage.com \
      -e FLATPIX_S3_REGION=auto \
      -e FLATPIX_S3_ACCESS_KEY=... -e FLATPIX_S3_SECRET_KEY=... \
      -e FLATPIX_CACHE=s3://my-media/derived \
      ghcr.io/taktekhq/flatpix
    For AWS S3, leave out the endpoint and set the region. Without keys, Flatpix reads public objects.
  4. Request one picture. /demo/image/upload/w_400,f_auto/products/shoe.jpg reads originals/products/shoe.jpg, returns a 400 pixel wide AVIF or WebP, and stores it under derived/.
  5. Put a CDN in front. The image server is the origin for your CDN, not a CDN itself. Point a CDN at it, or serve it on a Cloudflare-proxied hostname.

Caching and costs

Every image service that reads from your bucket pays for it in reads. Three habits keep that small:

  • Version your URLs. A version segment such as v1712 tells the server the file never changes. Flatpix then caches the result for a year and a cache hit never touches the bucket. Without a version, Flatpix makes one HEAD request to the bucket per request to check the file, which is why a CDN in front matters.
  • Keep a derived cache. Render once, store the result, serve it from storage after that. In Flatpix's benchmark, cached images were served at 6,466 requests a second against 8.2 fresh renders a second on the same machine.
  • Choose storage with free egress if the image server is elsewhere. R2 does not charge for data sent out. On S3, reads from a server outside AWS pay data transfer, so run the image server in the same region or put the CDN close to the bucket.

The image service itself charges in one of three ways: credits or usage (imgix, ImageKit), per unique transformation (Cloudflare), or your own servers (imgproxy, self-hosted Flatpix). Hosted Flatpix is the flat-price option: Starter $39 a month for 25,000 originals and 500 GB delivered, Growth $99 for 250,000 and 2 TB, Scale $249 for unlimited originals and 10 TB, with no credits and no overage bills.

Your bucket, one flat price

Flatpix reads your S3 or R2 bucket and serves Cloudinary-style URLs. A flat $39, $99 or $249 a month, or free to run yourself. Your first month is free.

Questions

Can an image CDN read directly from my S3 bucket?

Yes. imgix, ImageKit, imgproxy and Flatpix all read originals from an S3 bucket and resize them on request, so nothing is uploaded to a separate media library. Cloudflare's image transformations do the same for images served from your Cloudflare zone, including an R2 bucket on your domain.

Does Cloudflare R2 work as an image origin?

Yes. R2 is S3-compatible, so imgix, ImageKit through S3-compatible storage, imgproxy and Flatpix can read it with an endpoint and keys. R2 does not charge for data sent to the internet, which keeps origin reads cheap when the image server runs elsewhere.

How do I resize images stored in S3 without Cloudinary?

Run an image server that reads S3, such as imgproxy or Flatpix, or connect the bucket to a hosted service such as imgix or ImageKit. Give it a read-only key, request a URL with a width, and put a CDN in front. Flatpix keeps Cloudinary's URL format, so existing Cloudinary URLs keep working.

Where should the resized images be cached?

In two places: a CDN in front of the image server, and a derived cache that stores each rendered size. Flatpix can keep its derived cache in the same bucket under its own prefix, so renders survive restarts and a lifecycle rule can clear them.

What permissions does the image server need on my bucket?

Read access to the originals. If it stores its cache in the same bucket, it also needs write access to the cache prefix only. Keep originals and cache under separate prefixes so the server can never overwrite an original.

Sources

All pages were read on 31 October 2026. Products and prices change, so confirm them at the source.

  1. imgix: Creating sources. The storage types imgix connects to.
  2. ImageKit: Connect external storage. The external storage types ImageKit reads.
  3. ImageKit: Cloudinary backup bucket. Using a Cloudinary backup bucket as an origin.
  4. Cloudflare Images: sources. Which source images Cloudflare transforms by default, and allowed origins.
  5. imgproxy: Amazon S3. The s3:// source URL and the S3-compatible endpoint setting.
  6. Cloudflare R2: Pricing. Storage price and free egress.
  7. Flatpix on GitHub. Origins, the derived cache, cache headers and the benchmark.