Deploy Imagor
Image processing server that resizes and converts images from a URL
imagor
Just deployed
imagor-result
Bucket
Just deployed
imagor-source
Bucket
Just deployed
Deploy and Host Imagor on Railway
Imagor is an open-source image processing server written in Go on top of libvips, the engine behind many commercial image CDNs. Every transformation lives in the URL — fit-in/800x600/filters:format(webp):quality(85)/photo.jpg — so your front-end never pre-generates thumbnails or stores a dozen variants of one asset. It speaks the thumbor URL syntax, making it a drop-in replacement for a thumbor install, and teams self-host Imagor for Cloudinary-style on-the-fly resizing without per-image pricing.
This template runs Imagor as a public HTTP service backed by two Railway object storage buckets. imagor is the only service with a domain: it takes a signed request, loads the original from the imagor-source bucket (or fetches it over HTTP when the path is a remote URL), renders the variant with libvips, and writes the result to the imagor-result bucket so the next identical request is served from cache. Remote originals are saved into imagor-source too, so each source is fetched once. The app tier keeps nothing on disk, which is what lets you raise the replica count later.

Getting Started with Imagor on Railway
There is no login screen and no admin account. Open the deployed URL and you should see the Imagor landing page with the running version, confirming the server is up; /healthcheck returns 200 for uptime monitoring. Because IMAGOR_SECRET is generated at deploy time, every image path must carry an HMAC-SHA1 signature, so an unsigned URL gets 403 url signature mismatch rather than serving anyone who guesses a path. Copy IMAGOR_SECRET from the service variables and sign the part of the path after the signature segment:
IMAGOR_SECRET=""
P='fit-in/800x600/filters:format(webp):quality(85)/photo.jpg'
SIG=$(printf %s "$P" | openssl dgst -sha1 -hmac "$IMAGOR_SECRET" -binary | base64 | tr '+/' '-_')
echo "https:///$SIG/$P"
For your own images, upload them to imagor-source under the source/ prefix with any S3 client and reference them by filename; or put a full https:// image URL at the end of the path and Imagor fetches and caches it. Signing helpers exist for Go, Node, Python, PHP and Ruby, so a real application builds these URLs in its backend.

About Hosting Imagor
Self-hosting Imagor makes sense as soon as image delivery becomes a real line item. A commercial image CDN charges per transformation and owns the URL your pages point at; Imagor puts that layer in your own infrastructure, in front of storage you control, for the cost of the container rendering the images.
- Resize, crop, fit-in, stretch, pad, rotate and flip from URL parameters
- Content-aware smart cropping that keeps a photo's subject in frame
- Filters for quality, format, fill, blur, sharpen, grayscale, hue, rounded corners and watermarks
- JPEG, PNG, WebP, AVIF, GIF and TIFF output with automatic WebP/AVIF negotiation, animation included
- HMAC URL signing (SHA1, SHA256 or SHA512), and storage backends for S3, Google Cloud Storage, local disk or plain HTTP
The topology is three pieces. imagor is the stateless renderer. imagor-source holds originals and caches remote fetches. imagor-result holds rendered variants keyed by a digest of the request and is safe to empty at any time, so clearing the derived cache never touches your originals.
Why Deploy Imagor on Railway
Railway removes the parts of running an image server that are not about images:
- Both object storage buckets are provisioned and wired in for you
- A public HTTPS domain and certificate with no configuration
- The signing secret is generated at deploy time, so nothing ships unprotected
- Health checks and log aggregation as standard
- Raise replicas as traffic grows; the service holds no local state
Common Use Cases
- Serving responsive
srcsetvariants from one uploaded original - Generating product thumbnails and gallery crops for an e-commerce catalogue
- Replacing a paid image CDN's transformation tier while keeping your own storage
- Watermarking user-submitted media at request time
Imagor vs Thumbor vs imgproxy
| Imagor | thumbor | imgproxy | |
|---|---|---|---|
| Engine | Go + libvips | Python + PIL | Go + libvips |
| Licence | Apache-2.0 | MIT | Open core, paid Pro tier |
| URL syntax | thumbor-compatible | original | its own |
| Paid features | none | none | advanced filters, internal cache |
Imagor's published benchmarks put it ahead of thumbor on JPEG, PNG, WebP and AVIF, and trading places with imgproxy by format. Against imgproxy the practical difference is licensing: no Pro edition holds back filters or caching.
Dependencies for Imagor
shumc/imagor:latest— the Imagor server, Go and libvips in one imageimagor-source— bucket holding originals undersource/imagor-result— bucket holding rendered variants underresult/
Environment Variables Reference
| Variable | Purpose |
|---|---|
IMAGOR_SECRET | HMAC key for URL signing; generated at deploy time |
PORT | HTTP listening port, 8000 |
IMAGOR_AUTO_WEBP / IMAGOR_AUTO_AVIF | Serve WebP or AVIF when the browser allows it |
IMAGOR_PROCESS_CONCURRENCY | Ceiling on simultaneous libvips operations |
IMAGOR_CACHE_HEADER_TTL | Cache-Control max-age on successful responses |
S3_LOADER_BUCKET / S3_STORAGE_BUCKET | Bucket for reading and writing originals |
S3_RESULT_STORAGE_BUCKET | Bucket for rendered variants |
HTTP_LOADER_BLOCK_PRIVATE_NETWORKS | Refuses origin fetches aimed at internal addresses |
VIPS_STRIP_METADATA | Drops EXIF, including GPS, from output |
Deployment Dependencies
- Source: github.com/cshum/imagor
- Image: shumc/imagor on Docker Hub
- Docs: docs.imagor.net
Hardware Requirements for Self-Hosting Imagor
| Minimum | Recommended | |
|---|---|---|
| CPU | 1 vCPU | 2–4 vCPU |
| RAM | 512 MB | 2 GB |
| Storage | None on the container | Object storage sized to your media |
| Runtime | Docker | Docker |
libvips streams rather than buffering whole images, so memory stays modest and CPU decides throughput. AVIF encoding is the expensive case — raise VIPS_AVIF_SPEED or stay on WebP if requests queue up.
Is Imagor Free to Self-Host?
Imagor is free and open source under Apache-2.0, with no paid edition and no feature held back — every filter, format and storage backend is in the public image. On Railway you pay for the compute the renderer uses and the storage your media occupies, so cost tracks traffic rather than image count.
Self-Hosting Imagor
To try Imagor locally first, this starts an unsigned development server:
docker run -p 8000:8000 shumc/imagor -imagor-unsafe -imagor-auto-webp
Then open http://localhost:8000/unsafe/400x400/raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png. To run it as this template does — signed URLs against S3-compatible storage — pass the settings as variables:
docker run -p 8000:8000 \
-e IMAGOR_SECRET=your-signing-key -e S3_FORCE_PATH_STYLE=1 \
-e AWS_ACCESS_KEY_ID=... -e AWS_SECRET_ACCESS_KEY=... -e AWS_REGION=auto \
-e S3_ENDPOINT=https://your-s3-endpoint \
-e S3_LOADER_BUCKET=originals -e S3_RESULT_STORAGE_BUCKET=variants \
shumc/imagor
Never set -imagor-unsafe on a public instance: it turns signature checking off and lets anyone run transformations through your server.
FAQ
What is Imagor? An open-source image processing server built on libvips. It resizes, crops, converts and filters images on demand, with the operations written into the request URL, in thumbor's syntax.
What does this Railway template deploy?
One public imagor service on the official shumc/imagor image, plus the imagor-source and imagor-result object storage buckets.
Why does the template include two object storage buckets?
Separating them lets you clear the derived cache without risking your originals: imagor-result holds only regenerable images, so emptying it costs a little CPU and nothing else.
How do I generate a signed Imagor URL in my application?
Compute an HMAC of the path following the signature segment, keyed with IMAGOR_SECRET, and encode it as URL-safe base64. The default algorithm is SHA1; IMAGOR_SIGNER_TYPE switches it to SHA256 or SHA512.
Can Imagor process images from a URL instead of a bucket?
Yes. Put a full image URL at the end of the path and the HTTP loader fetches it, caches the original in imagor-source and renders from there. Restrict the origins it may reach with HTTP_LOADER_ALLOWED_SOURCES.
Does self-hosted Imagor support smart cropping and watermarks?
Both, with no paid tier. Add smart to the path for content-aware cropping, and use the watermark() filter to composite another image.
How do I scale Imagor for heavy traffic?
The service stores nothing locally, so raise the replica count and every instance shares one result cache. Put a CDN in front too, since responses already carry a long Cache-Control max-age.
Template Content